@n-uf/hypr-tiling 26.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +145 -0
- package/README.md +242 -0
- package/dist/chunk-ZCGZOWOY.mjs +9871 -0
- package/dist/devtools.cjs +12001 -0
- package/dist/devtools.d.mts +111 -0
- package/dist/devtools.d.ts +111 -0
- package/dist/devtools.mjs +2286 -0
- package/dist/drag-easing-5WbK3T82.d.ts +605 -0
- package/dist/drag-easing-KxPPNNZT.d.mts +605 -0
- package/dist/engine.cjs +9899 -0
- package/dist/engine.d.mts +314 -0
- package/dist/engine.d.ts +314 -0
- package/dist/engine.mjs +116 -0
- package/dist/index.cjs +9833 -0
- package/dist/index.d.mts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.mjs +125 -0
- package/dist/tiling-renderer-kTlSm4H4.d.mts +2185 -0
- package/dist/tiling-renderer-kTlSm4H4.d.ts +2185 -0
- package/package.json +96 -0
|
@@ -0,0 +1,2286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_DRAG_HOP_EASING,
|
|
3
|
+
DEFAULT_GHOST_PICKUP_SCALE_PERCENT,
|
|
4
|
+
DRAG_ANIMATION_SPEED_MAX_PERCENT,
|
|
5
|
+
DRAG_ANIMATION_SPEED_MIN_PERCENT,
|
|
6
|
+
GHOST_PICKUP_SCALE_MAX_PERCENT,
|
|
7
|
+
GHOST_PICKUP_SCALE_MIN_PERCENT,
|
|
8
|
+
SWAP_BOUNCE_MAX_PERCENT,
|
|
9
|
+
SWAP_BOUNCE_MIN_PERCENT,
|
|
10
|
+
TILING_OBSERVABILITY_COLOR_DEFAULTS,
|
|
11
|
+
TILING_OBSERVABILITY_COLOR_ENABLE_DEFAULTS,
|
|
12
|
+
TilingRendererWithObservability,
|
|
13
|
+
insertLeafAdjacent,
|
|
14
|
+
moveLeafToRoot,
|
|
15
|
+
moveLeafToSplitContainer,
|
|
16
|
+
resolveDragAnimationDurationMs,
|
|
17
|
+
toggleSplitAxis,
|
|
18
|
+
usePrefersReducedMotion
|
|
19
|
+
} from "./chunk-ZCGZOWOY.mjs";
|
|
20
|
+
|
|
21
|
+
// react/tiling-observability-panel.tsx
|
|
22
|
+
import * as React from "react";
|
|
23
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
24
|
+
var ANIMATION_CONTROL_DEFAULTS = {
|
|
25
|
+
speedLinked: true,
|
|
26
|
+
ghostTransitSpeedPercent: 100,
|
|
27
|
+
survivorReflowSpeedPercent: 100,
|
|
28
|
+
swapBounceMagnitudePercent: 30,
|
|
29
|
+
ghostPickupScalePercent: DEFAULT_GHOST_PICKUP_SCALE_PERCENT,
|
|
30
|
+
coherentTransit: true
|
|
31
|
+
};
|
|
32
|
+
var DRAG_HOP_EASING_PRESETS = [
|
|
33
|
+
{ id: "snappy", label: "snappy", value: DEFAULT_DRAG_HOP_EASING },
|
|
34
|
+
{ id: "linear", label: "linear", value: "linear" },
|
|
35
|
+
{ id: "ease-out", label: "ease out", value: "ease-out" },
|
|
36
|
+
{ id: "overshoot", label: "overshoot", value: "cubic-bezier(0.34, 1.56, 0.64, 1)" }
|
|
37
|
+
];
|
|
38
|
+
var LIVE_LEDGER_RETENTION_LIMIT = 60;
|
|
39
|
+
var CONTROL_PANE_WIDTH_PX = 320;
|
|
40
|
+
var CONTROL_PANE_STATUS_LINE_HEIGHT_PX = 16;
|
|
41
|
+
var CONTROL_PANE_STATUS_BADGE_WIDTH_CLASS = "w-20";
|
|
42
|
+
var CONTROL_PANE_INTENT_BADGE_RESERVED_ROWS = 1;
|
|
43
|
+
var CONTROL_PANE_INTENT_BADGE_RESERVED_HEIGHT_PX = CONTROL_PANE_STATUS_LINE_HEIGHT_PX * CONTROL_PANE_INTENT_BADGE_RESERVED_ROWS;
|
|
44
|
+
var CONTROL_PANE_REASON_RESERVED_ROWS = 3;
|
|
45
|
+
var CONTROL_PANE_REASON_RESERVED_HEIGHT_PX = CONTROL_PANE_STATUS_LINE_HEIGHT_PX * CONTROL_PANE_REASON_RESERVED_ROWS;
|
|
46
|
+
var RESIZE_CAPABILITY_OPTIONS = [
|
|
47
|
+
{ value: "both", label: "entire", title: "Resize every divider (width + height)" },
|
|
48
|
+
{ value: "horizontal", label: "hor", title: "Resize only width dividers (side-by-side panes, x-axis)" },
|
|
49
|
+
{ value: "vertical", label: "ver", title: "Resize only height dividers (stacked panes, y-axis)" },
|
|
50
|
+
{ value: "none", label: "none", title: "Disable all divider resizing" }
|
|
51
|
+
];
|
|
52
|
+
var DRAG_MODE_OPTIONS = [
|
|
53
|
+
{ value: "live", label: "live", title: "Hyprland-style detach: source leaf is detached on pickup, the remaining tree reflows once to close the gap, a ghost follows the cursor, and the drop commits once on release (reverts on cancel/Escape)" },
|
|
54
|
+
{ value: "preview", label: "preview", title: "Non-committing preview via translucent projected landing overlays (S' / T' / successor); layout tree untouched until drop" }
|
|
55
|
+
];
|
|
56
|
+
var SLOT_COMMITMENT_OPTIONS = [
|
|
57
|
+
{ value: "delta-responsive", label: "delta", title: "Delta-responsive (default): after the ghost hops into a slot, it re-evaluates the target eagerly once the cursor travels beyond the re-aim delta (24px) from the seat anchor \u2014 re-aim without fully exiting the pane" },
|
|
58
|
+
{ value: "zone-exit-hold", label: "hold", title: "Zone-exit hold (anchored / sticky): after the ghost hops into a slot it stays pinned through small cursor movements; the target re-resolves only when the cursor crosses OUT of the seated pane's hit zone" }
|
|
59
|
+
];
|
|
60
|
+
var LIVE_STATUS_VISIBILITY_OPTIONS = [
|
|
61
|
+
{ value: false, label: "off", title: "Hide the live status readout (default)" },
|
|
62
|
+
{ value: true, label: "on", title: "Show the live status readout (mode / drag mode / ghost / source / target / intent / validity / telemetry / blocked reason)" }
|
|
63
|
+
];
|
|
64
|
+
function keyChordModifierPrefix(modifiers) {
|
|
65
|
+
const parts = [
|
|
66
|
+
modifiers.ctrl ? "Ctrl" : null,
|
|
67
|
+
modifiers.alt ? "Alt" : null,
|
|
68
|
+
modifiers.shift ? "Shift" : null,
|
|
69
|
+
modifiers.meta ? "Meta" : null
|
|
70
|
+
].filter((part) => part != null);
|
|
71
|
+
return parts.join("+");
|
|
72
|
+
}
|
|
73
|
+
function formatKeyModifiersLabel(modifiers) {
|
|
74
|
+
const prefix = keyChordModifierPrefix(modifiers);
|
|
75
|
+
return prefix.length === 0 ? "(none)" : prefix;
|
|
76
|
+
}
|
|
77
|
+
function formatKeyCodeLabel(code) {
|
|
78
|
+
if (code === "BracketLeft") {
|
|
79
|
+
return "[";
|
|
80
|
+
}
|
|
81
|
+
if (code === "BracketRight") {
|
|
82
|
+
return "]";
|
|
83
|
+
}
|
|
84
|
+
if (code === "Escape") {
|
|
85
|
+
return "Esc";
|
|
86
|
+
}
|
|
87
|
+
const digitMatch = /^Digit([0-9])$/.exec(code);
|
|
88
|
+
if (digitMatch != null) {
|
|
89
|
+
return digitMatch[1];
|
|
90
|
+
}
|
|
91
|
+
return code;
|
|
92
|
+
}
|
|
93
|
+
function formatKeyChordLabel(chord) {
|
|
94
|
+
const prefix = keyChordModifierPrefix(chord);
|
|
95
|
+
const keyLabel = formatKeyCodeLabel(chord.code);
|
|
96
|
+
return prefix.length === 0 ? keyLabel : `${prefix}+${keyLabel}`;
|
|
97
|
+
}
|
|
98
|
+
function axisPathLabel(axisPath) {
|
|
99
|
+
if (axisPath.length === 0) {
|
|
100
|
+
return "none";
|
|
101
|
+
}
|
|
102
|
+
return axisPath.join(" -> ");
|
|
103
|
+
}
|
|
104
|
+
function normalizeReasonToken(reason) {
|
|
105
|
+
if (reason == null || reason.trim().length === 0) {
|
|
106
|
+
return "none";
|
|
107
|
+
}
|
|
108
|
+
if (reason === "none") {
|
|
109
|
+
return "none";
|
|
110
|
+
}
|
|
111
|
+
return reason.replace(/[-_]+/g, " ").toLowerCase();
|
|
112
|
+
}
|
|
113
|
+
function normalizeReasonList(reasons) {
|
|
114
|
+
if (reasons.length === 0) {
|
|
115
|
+
return "none";
|
|
116
|
+
}
|
|
117
|
+
return reasons.map((reason) => normalizeReasonToken(reason)).join(" | ");
|
|
118
|
+
}
|
|
119
|
+
function readIntentToken(input) {
|
|
120
|
+
const isBlocked = input.validityLabel === "blocked" || input.action === "none" && input.blockedReasonLabel !== "none";
|
|
121
|
+
if (isBlocked) {
|
|
122
|
+
return "blocked";
|
|
123
|
+
}
|
|
124
|
+
if (input.action === "swap") {
|
|
125
|
+
return "swap";
|
|
126
|
+
}
|
|
127
|
+
if (input.action === "edge-insert") {
|
|
128
|
+
if (input.edgeLabel === "left") {
|
|
129
|
+
return "left";
|
|
130
|
+
}
|
|
131
|
+
if (input.edgeLabel === "right") {
|
|
132
|
+
return "right";
|
|
133
|
+
}
|
|
134
|
+
if (input.edgeLabel === "top") {
|
|
135
|
+
return "top";
|
|
136
|
+
}
|
|
137
|
+
if (input.edgeLabel === "bottom") {
|
|
138
|
+
return "bottom";
|
|
139
|
+
}
|
|
140
|
+
return "split";
|
|
141
|
+
}
|
|
142
|
+
if (input.action === "split-container-insert") {
|
|
143
|
+
return "split";
|
|
144
|
+
}
|
|
145
|
+
return "none";
|
|
146
|
+
}
|
|
147
|
+
function readIntentBadgeClass(intentToken) {
|
|
148
|
+
if (intentToken === "blocked") {
|
|
149
|
+
return "bg-rose-500";
|
|
150
|
+
}
|
|
151
|
+
if (intentToken === "swap") {
|
|
152
|
+
return "bg-emerald-500";
|
|
153
|
+
}
|
|
154
|
+
if (intentToken === "left") {
|
|
155
|
+
return "bg-sky-500";
|
|
156
|
+
}
|
|
157
|
+
if (intentToken === "right") {
|
|
158
|
+
return "bg-violet-500";
|
|
159
|
+
}
|
|
160
|
+
if (intentToken === "top") {
|
|
161
|
+
return "bg-amber-500";
|
|
162
|
+
}
|
|
163
|
+
if (intentToken === "bottom") {
|
|
164
|
+
return "bg-teal-500";
|
|
165
|
+
}
|
|
166
|
+
if (intentToken === "split") {
|
|
167
|
+
return "bg-cyan-500";
|
|
168
|
+
}
|
|
169
|
+
return "bg-slate-500";
|
|
170
|
+
}
|
|
171
|
+
var GROUP_TOGGLE_OPTIONS = [
|
|
172
|
+
{ value: false, label: "off" },
|
|
173
|
+
{ value: true, label: "on" }
|
|
174
|
+
];
|
|
175
|
+
function SegmentedToggle(props) {
|
|
176
|
+
return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", role: "group", "aria-label": props.ariaLabel, children: GROUP_TOGGLE_OPTIONS.map((option) => {
|
|
177
|
+
const isActive = props.enabled === option.value;
|
|
178
|
+
return /* @__PURE__ */ jsx(
|
|
179
|
+
"button",
|
|
180
|
+
{
|
|
181
|
+
type: "button",
|
|
182
|
+
"aria-pressed": isActive,
|
|
183
|
+
title: option.value ? props.onTitle : props.offTitle,
|
|
184
|
+
onClick: () => props.onEnabledChange(option.value),
|
|
185
|
+
className: `rounded border px-2 py-0.5 font-mono text-[10px] uppercase tracking-[0.1em] transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300/55 focus-visible:ring-offset-1 focus-visible:ring-offset-slate-950 motion-reduce:transition-none ${isActive ? "border-cyan-300/70 bg-cyan-500/20 text-cyan-100" : "border-white/10 bg-slate-950 text-slate-400 hover:border-white/25"}`,
|
|
186
|
+
children: option.label
|
|
187
|
+
},
|
|
188
|
+
`${props.ariaLabel}-${option.label}`
|
|
189
|
+
);
|
|
190
|
+
}) });
|
|
191
|
+
}
|
|
192
|
+
function StickyPinToggle(props) {
|
|
193
|
+
return /* @__PURE__ */ jsx(
|
|
194
|
+
"button",
|
|
195
|
+
{
|
|
196
|
+
type: "button",
|
|
197
|
+
"aria-pressed": props.sticky,
|
|
198
|
+
"aria-label": props.ariaLabel,
|
|
199
|
+
title: "Pin this section above the scroll area so it stays visible (sticky). Off by default; toggles independently of on/off.",
|
|
200
|
+
onClick: () => props.onStickyChange(!props.sticky),
|
|
201
|
+
className: `rounded border px-2 py-0.5 font-mono text-[10px] uppercase tracking-[0.1em] transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-300/55 focus-visible:ring-offset-1 focus-visible:ring-offset-slate-950 motion-reduce:transition-none ${props.sticky ? "border-amber-300/70 bg-amber-500/20 text-amber-100" : "border-white/10 bg-slate-950 text-slate-400 hover:border-white/25"}`,
|
|
202
|
+
children: "pin"
|
|
203
|
+
}
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
function GroupToggleHeader(props) {
|
|
207
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
208
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 truncate font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: props.label }),
|
|
209
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
210
|
+
/* @__PURE__ */ jsx(
|
|
211
|
+
StickyPinToggle,
|
|
212
|
+
{
|
|
213
|
+
sticky: props.sticky,
|
|
214
|
+
onStickyChange: props.onStickyChange,
|
|
215
|
+
ariaLabel: `${props.ariaLabel} sticky`
|
|
216
|
+
}
|
|
217
|
+
),
|
|
218
|
+
/* @__PURE__ */ jsx(
|
|
219
|
+
SegmentedToggle,
|
|
220
|
+
{
|
|
221
|
+
enabled: props.enabled,
|
|
222
|
+
onEnabledChange: props.onEnabledChange,
|
|
223
|
+
ariaLabel: props.ariaLabel,
|
|
224
|
+
offTitle: props.offTitle,
|
|
225
|
+
onTitle: props.onTitle
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
] })
|
|
229
|
+
] });
|
|
230
|
+
}
|
|
231
|
+
var RANGE_SLIDER_STYLE = `
|
|
232
|
+
.hypr-range-scope input[type="range"].hypr-range {
|
|
233
|
+
-webkit-appearance: none;
|
|
234
|
+
appearance: none;
|
|
235
|
+
width: 100%;
|
|
236
|
+
height: 16px;
|
|
237
|
+
background: transparent;
|
|
238
|
+
cursor: pointer;
|
|
239
|
+
}
|
|
240
|
+
.hypr-range-scope input[type="range"].hypr-range:focus {
|
|
241
|
+
outline: none;
|
|
242
|
+
}
|
|
243
|
+
.hypr-range-scope input[type="range"].hypr-range::-webkit-slider-runnable-track {
|
|
244
|
+
height: 6px;
|
|
245
|
+
border-radius: 9999px;
|
|
246
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
247
|
+
background: linear-gradient(
|
|
248
|
+
to right,
|
|
249
|
+
rgba(34, 211, 238, 0.9) 0%,
|
|
250
|
+
rgba(34, 211, 238, 0.9) var(--hypr-range-fill, 0%),
|
|
251
|
+
rgba(148, 163, 184, 0.22) var(--hypr-range-fill, 0%),
|
|
252
|
+
rgba(148, 163, 184, 0.22) 100%
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
.hypr-range-scope input[type="range"].hypr-range::-webkit-slider-thumb {
|
|
256
|
+
-webkit-appearance: none;
|
|
257
|
+
appearance: none;
|
|
258
|
+
margin-top: -7px;
|
|
259
|
+
height: 16px;
|
|
260
|
+
width: 16px;
|
|
261
|
+
border-radius: 9999px;
|
|
262
|
+
background: #cffafe;
|
|
263
|
+
border: 2px solid rgba(34, 211, 238, 0.95);
|
|
264
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.45), 0 1px 3px rgba(0, 0, 0, 0.55);
|
|
265
|
+
transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease;
|
|
266
|
+
}
|
|
267
|
+
.hypr-range-scope input[type="range"].hypr-range:hover::-webkit-slider-thumb {
|
|
268
|
+
transform: scale(1.15);
|
|
269
|
+
}
|
|
270
|
+
.hypr-range-scope input[type="range"].hypr-range:active::-webkit-slider-thumb {
|
|
271
|
+
transform: scale(1.25);
|
|
272
|
+
background: #ffffff;
|
|
273
|
+
}
|
|
274
|
+
.hypr-range-scope input[type="range"].hypr-range:focus-visible::-webkit-slider-thumb {
|
|
275
|
+
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.45);
|
|
276
|
+
}
|
|
277
|
+
.hypr-range-scope input[type="range"].hypr-range::-moz-range-track {
|
|
278
|
+
height: 6px;
|
|
279
|
+
border-radius: 9999px;
|
|
280
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
281
|
+
background: rgba(148, 163, 184, 0.22);
|
|
282
|
+
}
|
|
283
|
+
.hypr-range-scope input[type="range"].hypr-range::-moz-range-progress {
|
|
284
|
+
height: 6px;
|
|
285
|
+
border-radius: 9999px;
|
|
286
|
+
background: rgba(34, 211, 238, 0.9);
|
|
287
|
+
}
|
|
288
|
+
.hypr-range-scope input[type="range"].hypr-range::-moz-range-thumb {
|
|
289
|
+
height: 16px;
|
|
290
|
+
width: 16px;
|
|
291
|
+
border-radius: 9999px;
|
|
292
|
+
background: #cffafe;
|
|
293
|
+
border: 2px solid rgba(34, 211, 238, 0.95);
|
|
294
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
|
|
295
|
+
transition: transform 120ms ease, box-shadow 120ms ease, background 120ms ease;
|
|
296
|
+
}
|
|
297
|
+
.hypr-range-scope input[type="range"].hypr-range:hover::-moz-range-thumb {
|
|
298
|
+
transform: scale(1.15);
|
|
299
|
+
}
|
|
300
|
+
.hypr-range-scope input[type="range"].hypr-range:active::-moz-range-thumb {
|
|
301
|
+
transform: scale(1.25);
|
|
302
|
+
background: #ffffff;
|
|
303
|
+
}
|
|
304
|
+
.hypr-range-scope input[type="range"].hypr-range:focus-visible::-moz-range-thumb {
|
|
305
|
+
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.45);
|
|
306
|
+
}
|
|
307
|
+
.hypr-range-scope input[type="range"].hypr-range:disabled {
|
|
308
|
+
cursor: not-allowed;
|
|
309
|
+
opacity: 0.4;
|
|
310
|
+
}
|
|
311
|
+
.hypr-range-scope input[type="range"].hypr-range:disabled::-webkit-slider-thumb {
|
|
312
|
+
transform: none;
|
|
313
|
+
}
|
|
314
|
+
.hypr-range-scope input[type="range"].hypr-range:disabled::-moz-range-thumb {
|
|
315
|
+
transform: none;
|
|
316
|
+
}
|
|
317
|
+
@media (prefers-reduced-motion: reduce) {
|
|
318
|
+
.hypr-range-scope input[type="range"].hypr-range::-webkit-slider-thumb,
|
|
319
|
+
.hypr-range-scope input[type="range"].hypr-range::-moz-range-thumb {
|
|
320
|
+
transition: none;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
`;
|
|
324
|
+
var STYLED_CHECKBOX_STYLE_HREF = "hypr-tiling-styled-checkbox";
|
|
325
|
+
var CHECKBOX_STYLE = `
|
|
326
|
+
.hpt-checkbox-box {
|
|
327
|
+
position: relative;
|
|
328
|
+
display: inline-flex;
|
|
329
|
+
height: 16px;
|
|
330
|
+
width: 16px;
|
|
331
|
+
flex: none;
|
|
332
|
+
align-items: center;
|
|
333
|
+
justify-content: center;
|
|
334
|
+
}
|
|
335
|
+
.hpt-checkbox {
|
|
336
|
+
position: absolute;
|
|
337
|
+
inset: 0;
|
|
338
|
+
margin: 0;
|
|
339
|
+
height: 100%;
|
|
340
|
+
width: 100%;
|
|
341
|
+
cursor: pointer;
|
|
342
|
+
-webkit-appearance: none;
|
|
343
|
+
-moz-appearance: none;
|
|
344
|
+
appearance: none;
|
|
345
|
+
border-radius: 4px;
|
|
346
|
+
border: 1px solid rgb(255 255 255 / 0.25);
|
|
347
|
+
background-color: rgb(2 6 23);
|
|
348
|
+
box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.45);
|
|
349
|
+
color-scheme: dark;
|
|
350
|
+
transition: background-color 150ms, border-color 150ms, box-shadow 150ms;
|
|
351
|
+
}
|
|
352
|
+
.hpt-checkbox:hover {
|
|
353
|
+
border-color: rgb(103 232 249 / 0.6);
|
|
354
|
+
}
|
|
355
|
+
.hpt-checkbox:checked {
|
|
356
|
+
background-color: rgb(34 211 238);
|
|
357
|
+
border-color: rgb(103 232 249);
|
|
358
|
+
box-shadow: 0 0 0 1px rgb(34 211 238 / 0.45);
|
|
359
|
+
}
|
|
360
|
+
.hpt-checkbox:focus-visible {
|
|
361
|
+
box-shadow: 0 0 0 2px rgb(34 211 238 / 0.65) !important;
|
|
362
|
+
}
|
|
363
|
+
.hpt-checkbox:disabled {
|
|
364
|
+
cursor: not-allowed;
|
|
365
|
+
opacity: 0.4;
|
|
366
|
+
}
|
|
367
|
+
.hpt-checkbox-tick {
|
|
368
|
+
position: relative;
|
|
369
|
+
height: 14px;
|
|
370
|
+
width: 14px;
|
|
371
|
+
color: rgb(2 6 23);
|
|
372
|
+
pointer-events: none;
|
|
373
|
+
opacity: 0;
|
|
374
|
+
transform: scale(0.5);
|
|
375
|
+
transition: opacity 150ms, transform 150ms;
|
|
376
|
+
}
|
|
377
|
+
.hpt-checkbox:checked ~ .hpt-checkbox-tick {
|
|
378
|
+
opacity: 1;
|
|
379
|
+
transform: scale(1);
|
|
380
|
+
}
|
|
381
|
+
@media (prefers-reduced-motion: reduce) {
|
|
382
|
+
.hpt-checkbox,
|
|
383
|
+
.hpt-checkbox-tick {
|
|
384
|
+
transition: none;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
`;
|
|
388
|
+
function StyledCheckbox(props) {
|
|
389
|
+
return /* @__PURE__ */ jsxs("span", { className: "hpt-checkbox-box", children: [
|
|
390
|
+
/* @__PURE__ */ jsx("style", { href: STYLED_CHECKBOX_STYLE_HREF, precedence: "default", children: CHECKBOX_STYLE }),
|
|
391
|
+
/* @__PURE__ */ jsx(
|
|
392
|
+
"input",
|
|
393
|
+
{
|
|
394
|
+
type: "checkbox",
|
|
395
|
+
className: "hpt-checkbox",
|
|
396
|
+
checked: props.checked,
|
|
397
|
+
disabled: props.disabled,
|
|
398
|
+
title: props.title,
|
|
399
|
+
"aria-label": props.ariaLabel,
|
|
400
|
+
onChange: (event) => props.onChange(event.target.checked)
|
|
401
|
+
}
|
|
402
|
+
),
|
|
403
|
+
/* @__PURE__ */ jsx("svg", { className: "hpt-checkbox-tick", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
|
|
404
|
+
"path",
|
|
405
|
+
{
|
|
406
|
+
d: "M3.5 8.5l3 3 6-7",
|
|
407
|
+
fill: "none",
|
|
408
|
+
stroke: "currentColor",
|
|
409
|
+
strokeWidth: 2.5,
|
|
410
|
+
strokeLinecap: "round",
|
|
411
|
+
strokeLinejoin: "round"
|
|
412
|
+
}
|
|
413
|
+
) })
|
|
414
|
+
] });
|
|
415
|
+
}
|
|
416
|
+
function RangeSlider(props) {
|
|
417
|
+
const span = props.max - props.min;
|
|
418
|
+
const ratio = span <= 0 ? 0 : (props.value - props.min) / span;
|
|
419
|
+
const clamped = Math.min(1, Math.max(0, ratio));
|
|
420
|
+
const fillStyle = { "--hypr-range-fill": `${clamped * 100}%` };
|
|
421
|
+
return /* @__PURE__ */ jsx(
|
|
422
|
+
"input",
|
|
423
|
+
{
|
|
424
|
+
type: "range",
|
|
425
|
+
min: props.min,
|
|
426
|
+
max: props.max,
|
|
427
|
+
step: props.step,
|
|
428
|
+
value: props.value,
|
|
429
|
+
disabled: props.disabled,
|
|
430
|
+
title: props.title,
|
|
431
|
+
"aria-label": props.ariaLabel,
|
|
432
|
+
style: fillStyle,
|
|
433
|
+
onChange: (event) => props.onChange(Number(event.target.value)),
|
|
434
|
+
className: "hypr-range w-full"
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
function SubjectColorRow(props) {
|
|
439
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
440
|
+
/* @__PURE__ */ jsxs("label", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
|
|
441
|
+
/* @__PURE__ */ jsx(
|
|
442
|
+
StyledCheckbox,
|
|
443
|
+
{
|
|
444
|
+
checked: props.enabled,
|
|
445
|
+
onChange: props.onEnabledChange,
|
|
446
|
+
title: `Enable ${props.label} overlay`
|
|
447
|
+
}
|
|
448
|
+
),
|
|
449
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: props.label })
|
|
450
|
+
] }),
|
|
451
|
+
/* @__PURE__ */ jsx(
|
|
452
|
+
"input",
|
|
453
|
+
{
|
|
454
|
+
type: "color",
|
|
455
|
+
value: props.color,
|
|
456
|
+
disabled: !props.enabled,
|
|
457
|
+
onChange: (event) => props.onColorChange(event.target.value),
|
|
458
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950 disabled:opacity-40"
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
] });
|
|
462
|
+
}
|
|
463
|
+
function hasEnabledProjectedFill(enables) {
|
|
464
|
+
return enables.projectedSourceFillEnabled || enables.projectedTargetFillEnabled || enables.projectedSuccessorFillEnabled;
|
|
465
|
+
}
|
|
466
|
+
function TilingObservabilityPanel(props) {
|
|
467
|
+
const isLiveDragModeActive = props.interactionCapabilities.dragMode === "live";
|
|
468
|
+
const isDraggingActive = props.liveHitLog?.isDragging === true || props.liveDropIntent != null;
|
|
469
|
+
const isHoveringActive = props.liveHitLog != null && !isDraggingActive;
|
|
470
|
+
const isLiveStatusIdle = !isDraggingActive && !isHoveringActive;
|
|
471
|
+
const liveModeLabel = isLiveStatusIdle ? "idle" : isDraggingActive ? "dragging" : "hovering";
|
|
472
|
+
const liveGhostLabel = !isLiveDragModeActive ? "n/a (preview)" : isDraggingActive && props.liveHitLog?.dragSourceLeafId != null ? `detached ${props.liveHitLog.dragSourceLeafId}` : "idle";
|
|
473
|
+
const liveSourceLabel = isLiveStatusIdle ? "none" : isDraggingActive ? props.liveHitLog?.sourceLeafId ?? "none" : props.liveHitLog?.sourceLeafId ?? "none";
|
|
474
|
+
const liveTargetLabel = isLiveStatusIdle ? "none" : isDraggingActive ? props.liveDropIntent?.leafId ?? props.liveHitLog?.hoveredLeafId ?? "none" : props.liveHitLog?.hoveredLeafId ?? "none";
|
|
475
|
+
const liveIntentAction = isLiveStatusIdle ? "none" : props.liveDropIntent?.action ?? props.liveHitLog?.intent?.action ?? "none";
|
|
476
|
+
const liveIntentEdgeLabel = isLiveStatusIdle ? "none" : props.liveDropIntent?.finalEdge ?? props.liveDropIntent?.selectedSplitZone ?? props.liveHitLog?.intent?.finalEdge ?? props.liveHitLog?.intent?.selectedSplitZone ?? "none";
|
|
477
|
+
const liveValidityLabel = isLiveStatusIdle ? "none" : props.liveHitLog?.centerIsValid == null ? liveIntentAction === "none" ? "none" : "unknown" : props.liveHitLog.centerIsValid ? "valid" : "blocked";
|
|
478
|
+
const liveBlockedReasonLabel = isLiveStatusIdle ? "none" : props.liveHitLog?.centerBlockedReason ?? props.liveDropIntent?.blockedReason ?? props.liveHitLog?.intent?.blockedReason ?? "none";
|
|
479
|
+
const liveIntentToken = readIntentToken({
|
|
480
|
+
action: liveIntentAction,
|
|
481
|
+
edgeLabel: liveIntentEdgeLabel,
|
|
482
|
+
blockedReasonLabel: liveBlockedReasonLabel,
|
|
483
|
+
validityLabel: liveValidityLabel
|
|
484
|
+
});
|
|
485
|
+
const liveIntentBadgeClass = readIntentBadgeClass(liveIntentToken);
|
|
486
|
+
const liveCursorXLabel = props.liveHitLog == null ? "none" : props.liveHitLog.cursorViewport.x.toFixed(1);
|
|
487
|
+
const liveCursorYLabel = props.liveHitLog == null ? "none" : props.liveHitLog.cursorViewport.y.toFixed(1);
|
|
488
|
+
const sourcePaneFootprint = props.liveHitLog?.sourcePaneFootprint ?? null;
|
|
489
|
+
const liveSourceXLabel = sourcePaneFootprint == null ? "none" : sourcePaneFootprint.left.toFixed(1);
|
|
490
|
+
const liveSourceYLabel = sourcePaneFootprint == null ? "none" : sourcePaneFootprint.top.toFixed(1);
|
|
491
|
+
const liveSourceXPlusWLabel = sourcePaneFootprint == null ? "none" : (sourcePaneFootprint.left + sourcePaneFootprint.width).toFixed(1);
|
|
492
|
+
const liveSourceYPlusHLabel = sourcePaneFootprint == null ? "none" : (sourcePaneFootprint.top + sourcePaneFootprint.height).toFixed(1);
|
|
493
|
+
const updateObservabilityColor = React.useCallback(
|
|
494
|
+
(field, value) => {
|
|
495
|
+
props.setObservabilityColors((previous) => ({
|
|
496
|
+
...previous,
|
|
497
|
+
[field]: value
|
|
498
|
+
}));
|
|
499
|
+
},
|
|
500
|
+
[props]
|
|
501
|
+
);
|
|
502
|
+
const updateObservabilityColorEnable = React.useCallback(
|
|
503
|
+
(field, value) => {
|
|
504
|
+
props.setObservabilityColorEnables(
|
|
505
|
+
(previous) => ({
|
|
506
|
+
...previous,
|
|
507
|
+
[field]: value
|
|
508
|
+
})
|
|
509
|
+
);
|
|
510
|
+
},
|
|
511
|
+
[props]
|
|
512
|
+
);
|
|
513
|
+
const projectedFillAlphaEnabled = hasEnabledProjectedFill(props.observabilityColorEnables);
|
|
514
|
+
const prefersReducedMotion = usePrefersReducedMotion();
|
|
515
|
+
const resolvedGhostTransitDurationMs = resolveDragAnimationDurationMs(props.ghostTransitSpeedPercent);
|
|
516
|
+
const resolvedSurvivorReflowDurationMs = resolveDragAnimationDurationMs(props.survivorReflowSpeedPercent);
|
|
517
|
+
const liveStatusReadout = /* @__PURE__ */ jsxs("section", { className: "shrink-0 min-w-0 max-w-full overflow-hidden rounded border border-cyan-300/40 bg-slate-900/90 p-1.5", children: [
|
|
518
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[9px] uppercase tracking-[0.12em] text-cyan-200", children: "live status" }),
|
|
519
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1.5 grid grid-cols-2 gap-x-2 gap-y-0.5 font-mono text-[10px] uppercase tracking-[0.12em]", children: [
|
|
520
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", children: "mode" }),
|
|
521
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-200", children: liveModeLabel }),
|
|
522
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", title: "Drag feedback mode: preview (projected S'/T'/successor overlays) vs live (Hyprland detach: source detached on pickup, ghost follows cursor, commit on release)", children: "drag mode" }),
|
|
523
|
+
/* @__PURE__ */ jsx("div", { className: isLiveDragModeActive ? "text-cyan-200" : "text-slate-200", children: props.interactionCapabilities.dragMode }),
|
|
524
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", title: "Live (Hyprland) ghost: the dragged source detached from the frozen tree and following the cursor", children: "ghost" }),
|
|
525
|
+
/* @__PURE__ */ jsx(
|
|
526
|
+
"div",
|
|
527
|
+
{
|
|
528
|
+
className: isLiveDragModeActive && isDraggingActive && props.liveHitLog?.dragSourceLeafId != null ? "text-cyan-200" : "text-slate-200",
|
|
529
|
+
title: "Live (Hyprland) ghost: the dragged source detached from the frozen tree and following the cursor",
|
|
530
|
+
children: liveGhostLabel
|
|
531
|
+
}
|
|
532
|
+
),
|
|
533
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", title: "Pane currently under the cursor", children: "source" }),
|
|
534
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-200", title: "Pane currently under the cursor", children: liveSourceLabel }),
|
|
535
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", title: "Drop resolver hover target", children: "target" }),
|
|
536
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-200", title: "Drop resolver hover target", children: liveTargetLabel }),
|
|
537
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", children: "intent" }),
|
|
538
|
+
/* @__PURE__ */ jsx(
|
|
539
|
+
"div",
|
|
540
|
+
{
|
|
541
|
+
className: "min-w-0 max-w-full overflow-hidden",
|
|
542
|
+
style: {
|
|
543
|
+
height: CONTROL_PANE_INTENT_BADGE_RESERVED_HEIGHT_PX,
|
|
544
|
+
minHeight: CONTROL_PANE_INTENT_BADGE_RESERVED_HEIGHT_PX,
|
|
545
|
+
maxHeight: CONTROL_PANE_INTENT_BADGE_RESERVED_HEIGHT_PX,
|
|
546
|
+
lineHeight: CONTROL_PANE_STATUS_LINE_HEIGHT_PX / 16
|
|
547
|
+
},
|
|
548
|
+
children: /* @__PURE__ */ jsx(
|
|
549
|
+
"span",
|
|
550
|
+
{
|
|
551
|
+
className: `${CONTROL_PANE_STATUS_BADGE_WIDTH_CLASS} ${liveIntentBadgeClass} inline-flex max-w-full items-center justify-center rounded px-2 py-0.5 font-semibold uppercase tracking-[0.12em] text-white whitespace-nowrap`,
|
|
552
|
+
children: liveIntentToken
|
|
553
|
+
}
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
),
|
|
557
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", children: "validity" }),
|
|
558
|
+
/* @__PURE__ */ jsx("div", { className: liveValidityLabel === "blocked" ? "text-rose-200" : "text-emerald-200", children: liveValidityLabel })
|
|
559
|
+
] }),
|
|
560
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1.5 min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 px-1.5 py-0.5 text-slate-200", children: [
|
|
561
|
+
/* @__PURE__ */ jsx("div", { className: "mb-0.5 font-mono text-[9px] uppercase tracking-[0.11em] text-slate-500", children: "telemetry" }),
|
|
562
|
+
/* @__PURE__ */ jsxs(
|
|
563
|
+
"div",
|
|
564
|
+
{
|
|
565
|
+
className: "grid w-full min-w-0 max-w-full gap-1",
|
|
566
|
+
style: {
|
|
567
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(118px, 1fr))"
|
|
568
|
+
},
|
|
569
|
+
children: [
|
|
570
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 w-full rounded border border-white/10 bg-slate-900/80 px-1.5 py-0.5", children: [
|
|
571
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[9px] uppercase tracking-[0.1em] text-slate-500", children: "cursor" }),
|
|
572
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 space-y-0.5", children: [
|
|
573
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
574
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-slate-500", children: "x" }),
|
|
575
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-x-auto whitespace-nowrap text-right text-[10px] tabular-nums", children: liveCursorXLabel })
|
|
576
|
+
] }),
|
|
577
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
578
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-slate-500", children: "y" }),
|
|
579
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-x-auto whitespace-nowrap text-right text-[10px] tabular-nums", children: liveCursorYLabel })
|
|
580
|
+
] })
|
|
581
|
+
] })
|
|
582
|
+
] }),
|
|
583
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 w-full rounded border border-white/10 bg-slate-900/80 px-1.5 py-0.5", children: [
|
|
584
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[9px] uppercase tracking-[0.1em] text-slate-500", children: "source pane" }),
|
|
585
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 space-y-0.5", children: [
|
|
586
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
587
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-slate-500", children: "x" }),
|
|
588
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-x-auto whitespace-nowrap text-right text-[10px] tabular-nums", children: liveSourceXLabel })
|
|
589
|
+
] }),
|
|
590
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
591
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-slate-500", children: "y" }),
|
|
592
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-x-auto whitespace-nowrap text-right text-[10px] tabular-nums", children: liveSourceYLabel })
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
595
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-slate-500", children: "x+w" }),
|
|
596
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-x-auto whitespace-nowrap text-right text-[10px] tabular-nums", children: liveSourceXPlusWLabel })
|
|
597
|
+
] }),
|
|
598
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
599
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-slate-500", children: "y+h" }),
|
|
600
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 overflow-x-auto whitespace-nowrap text-right text-[10px] tabular-nums", children: liveSourceYPlusHLabel })
|
|
601
|
+
] })
|
|
602
|
+
] })
|
|
603
|
+
] })
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
)
|
|
607
|
+
] }),
|
|
608
|
+
/* @__PURE__ */ jsxs(
|
|
609
|
+
"div",
|
|
610
|
+
{
|
|
611
|
+
className: "mt-1.5 min-w-0 max-w-full rounded border border-white/10 bg-slate-950/70 px-2 py-1 font-mono text-[10px] tracking-[0.12em] text-slate-300",
|
|
612
|
+
style: {
|
|
613
|
+
height: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
614
|
+
minHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
615
|
+
maxHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
616
|
+
lineHeight: CONTROL_PANE_STATUS_LINE_HEIGHT_PX / 16
|
|
617
|
+
},
|
|
618
|
+
children: [
|
|
619
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500 uppercase", children: "blocked reason:" }),
|
|
620
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 overflow-x-auto whitespace-nowrap normal-case", children: normalizeReasonToken(liveBlockedReasonLabel) })
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
)
|
|
624
|
+
] });
|
|
625
|
+
const previewOverlaysGroup = /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
626
|
+
/* @__PURE__ */ jsx(
|
|
627
|
+
GroupToggleHeader,
|
|
628
|
+
{
|
|
629
|
+
label: "preview overlays",
|
|
630
|
+
enabled: props.previewOverlaysEnabled,
|
|
631
|
+
onEnabledChange: props.setPreviewOverlaysEnabled,
|
|
632
|
+
sticky: props.previewOverlaysSticky,
|
|
633
|
+
onStickyChange: props.setPreviewOverlaysSticky,
|
|
634
|
+
ariaLabel: "preview overlays group",
|
|
635
|
+
offTitle: "Disable the projected overlay layers (S' / T' / successor landing geometry) and collapse the controls. Per-control states are preserved for re-enabling. Debugging aid: off by default.",
|
|
636
|
+
onTitle: "Enable the projected overlay layers and expand the controls (projected geometry layers + shared fill alpha)."
|
|
637
|
+
}
|
|
638
|
+
),
|
|
639
|
+
props.previewOverlaysEnabled ? /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
|
|
640
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300", title: "Show projected geometry overlays from reducer projection", children: [
|
|
641
|
+
/* @__PURE__ */ jsx(
|
|
642
|
+
StyledCheckbox,
|
|
643
|
+
{
|
|
644
|
+
checked: props.showDropPreviewOverlays,
|
|
645
|
+
onChange: props.setShowDropPreviewOverlays
|
|
646
|
+
}
|
|
647
|
+
),
|
|
648
|
+
"show projected overlay layers"
|
|
649
|
+
] }),
|
|
650
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
651
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
652
|
+
"projected overlay fill alpha (shared): ",
|
|
653
|
+
projectedFillAlphaEnabled ? `${props.projectedOverlayBgAlphaPercent}%` : "off (no fill enabled)"
|
|
654
|
+
] }),
|
|
655
|
+
/* @__PURE__ */ jsx(
|
|
656
|
+
RangeSlider,
|
|
657
|
+
{
|
|
658
|
+
min: 0,
|
|
659
|
+
max: 100,
|
|
660
|
+
step: 1,
|
|
661
|
+
disabled: !projectedFillAlphaEnabled,
|
|
662
|
+
value: props.projectedOverlayBgAlphaPercent,
|
|
663
|
+
onChange: props.setProjectedOverlayBgAlphaPercent,
|
|
664
|
+
title: "Shared fill transparency for enabled projected source/target/successor fills"
|
|
665
|
+
}
|
|
666
|
+
)
|
|
667
|
+
] })
|
|
668
|
+
] }) : null
|
|
669
|
+
] });
|
|
670
|
+
const subjectColorsGroup = /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
671
|
+
/* @__PURE__ */ jsx(
|
|
672
|
+
GroupToggleHeader,
|
|
673
|
+
{
|
|
674
|
+
label: "subject colors",
|
|
675
|
+
enabled: props.subjectColorsEnabled,
|
|
676
|
+
onEnabledChange: props.setSubjectColorsEnabled,
|
|
677
|
+
sticky: props.subjectColorsSticky,
|
|
678
|
+
onStickyChange: props.setSubjectColorsSticky,
|
|
679
|
+
ariaLabel: "subject colors group",
|
|
680
|
+
offTitle: "Disable all subject-color overlays (drag source/target borders and projected source/target/successor borders & fills) and collapse the controls. Per-color states are preserved for re-enabling. Debugging aid: off by default.",
|
|
681
|
+
onTitle: "Enable the subject-color overlays and expand the per-subject color/enable rows."
|
|
682
|
+
}
|
|
683
|
+
),
|
|
684
|
+
props.subjectColorsEnabled ? /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2", children: [
|
|
685
|
+
/* @__PURE__ */ jsx(
|
|
686
|
+
SubjectColorRow,
|
|
687
|
+
{
|
|
688
|
+
label: "drag source border",
|
|
689
|
+
enabled: props.observabilityColorEnables.dragSourceBorderEnabled,
|
|
690
|
+
color: props.observabilityColors.dragSourceBorderColorHex,
|
|
691
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("dragSourceBorderEnabled", enabled),
|
|
692
|
+
onColorChange: (color) => updateObservabilityColor("dragSourceBorderColorHex", color)
|
|
693
|
+
}
|
|
694
|
+
),
|
|
695
|
+
/* @__PURE__ */ jsx(
|
|
696
|
+
SubjectColorRow,
|
|
697
|
+
{
|
|
698
|
+
label: "drag target border",
|
|
699
|
+
enabled: props.observabilityColorEnables.dragTargetBorderEnabled,
|
|
700
|
+
color: props.observabilityColors.dragTargetBorderColorHex,
|
|
701
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("dragTargetBorderEnabled", enabled),
|
|
702
|
+
onColorChange: (color) => updateObservabilityColor("dragTargetBorderColorHex", color)
|
|
703
|
+
}
|
|
704
|
+
),
|
|
705
|
+
/* @__PURE__ */ jsx(
|
|
706
|
+
SubjectColorRow,
|
|
707
|
+
{
|
|
708
|
+
label: "projected source border",
|
|
709
|
+
enabled: props.observabilityColorEnables.projectedSourceBorderEnabled,
|
|
710
|
+
color: props.observabilityColors.projectedSourceBorderColorHex,
|
|
711
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("projectedSourceBorderEnabled", enabled),
|
|
712
|
+
onColorChange: (color) => updateObservabilityColor("projectedSourceBorderColorHex", color)
|
|
713
|
+
}
|
|
714
|
+
),
|
|
715
|
+
/* @__PURE__ */ jsx(
|
|
716
|
+
SubjectColorRow,
|
|
717
|
+
{
|
|
718
|
+
label: "projected target border",
|
|
719
|
+
enabled: props.observabilityColorEnables.projectedTargetBorderEnabled,
|
|
720
|
+
color: props.observabilityColors.projectedTargetBorderColorHex,
|
|
721
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("projectedTargetBorderEnabled", enabled),
|
|
722
|
+
onColorChange: (color) => updateObservabilityColor("projectedTargetBorderColorHex", color)
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
/* @__PURE__ */ jsx(
|
|
726
|
+
SubjectColorRow,
|
|
727
|
+
{
|
|
728
|
+
label: "projected source fill",
|
|
729
|
+
enabled: props.observabilityColorEnables.projectedSourceFillEnabled,
|
|
730
|
+
color: props.observabilityColors.projectedSourceFillColorHex,
|
|
731
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("projectedSourceFillEnabled", enabled),
|
|
732
|
+
onColorChange: (color) => updateObservabilityColor("projectedSourceFillColorHex", color)
|
|
733
|
+
}
|
|
734
|
+
),
|
|
735
|
+
/* @__PURE__ */ jsx(
|
|
736
|
+
SubjectColorRow,
|
|
737
|
+
{
|
|
738
|
+
label: "projected target fill",
|
|
739
|
+
enabled: props.observabilityColorEnables.projectedTargetFillEnabled,
|
|
740
|
+
color: props.observabilityColors.projectedTargetFillColorHex,
|
|
741
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("projectedTargetFillEnabled", enabled),
|
|
742
|
+
onColorChange: (color) => updateObservabilityColor("projectedTargetFillColorHex", color)
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
/* @__PURE__ */ jsx(
|
|
746
|
+
SubjectColorRow,
|
|
747
|
+
{
|
|
748
|
+
label: "projected successor border",
|
|
749
|
+
enabled: props.observabilityColorEnables.projectedSuccessorBorderEnabled,
|
|
750
|
+
color: props.observabilityColors.projectedSuccessorBorderColorHex,
|
|
751
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("projectedSuccessorBorderEnabled", enabled),
|
|
752
|
+
onColorChange: (color) => updateObservabilityColor("projectedSuccessorBorderColorHex", color)
|
|
753
|
+
}
|
|
754
|
+
),
|
|
755
|
+
/* @__PURE__ */ jsx(
|
|
756
|
+
SubjectColorRow,
|
|
757
|
+
{
|
|
758
|
+
label: "projected successor fill",
|
|
759
|
+
enabled: props.observabilityColorEnables.projectedSuccessorFillEnabled,
|
|
760
|
+
color: props.observabilityColors.projectedSuccessorFillColorHex,
|
|
761
|
+
onEnabledChange: (enabled) => updateObservabilityColorEnable("projectedSuccessorFillEnabled", enabled),
|
|
762
|
+
onColorChange: (color) => updateObservabilityColor("projectedSuccessorFillColorHex", color)
|
|
763
|
+
}
|
|
764
|
+
)
|
|
765
|
+
] }) : null
|
|
766
|
+
] });
|
|
767
|
+
const dropIntentDebugGroup = /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
768
|
+
/* @__PURE__ */ jsx(
|
|
769
|
+
GroupToggleHeader,
|
|
770
|
+
{
|
|
771
|
+
label: "drop intent debug",
|
|
772
|
+
enabled: props.dropIntentDebugEnabled,
|
|
773
|
+
onEnabledChange: props.setDropIntentDebugEnabled,
|
|
774
|
+
sticky: props.dropIntentDebugSticky,
|
|
775
|
+
onStickyChange: props.setDropIntentDebugSticky,
|
|
776
|
+
ariaLabel: "drop intent debug group",
|
|
777
|
+
offTitle: "Disable the in-pane drop-intent debug visuals (directional border hints, hint background, debug labels) and collapse the controls. Per-control states are preserved for re-enabling. Debugging aid: off by default.",
|
|
778
|
+
onTitle: "Enable the in-pane drop-intent debug visuals and expand the controls (border hints, hint background, debug labels)."
|
|
779
|
+
}
|
|
780
|
+
),
|
|
781
|
+
props.dropIntentDebugEnabled ? /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2", children: [
|
|
782
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300", title: "In-pane directional border hints (left/right/top/bottom/center). Requires drag target border enabled.", children: [
|
|
783
|
+
/* @__PURE__ */ jsx(
|
|
784
|
+
StyledCheckbox,
|
|
785
|
+
{
|
|
786
|
+
checked: props.showDropBorderHints,
|
|
787
|
+
onChange: props.setShowDropBorderHints
|
|
788
|
+
}
|
|
789
|
+
),
|
|
790
|
+
"show drop intent border hints"
|
|
791
|
+
] }),
|
|
792
|
+
/* @__PURE__ */ jsxs(
|
|
793
|
+
"label",
|
|
794
|
+
{
|
|
795
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
796
|
+
title: "Enable translucent fill for in-pane drop intent hints only (left/right/top/bottom/center)",
|
|
797
|
+
children: [
|
|
798
|
+
/* @__PURE__ */ jsx(
|
|
799
|
+
StyledCheckbox,
|
|
800
|
+
{
|
|
801
|
+
checked: props.showDropIntentTranslucentBg,
|
|
802
|
+
onChange: props.setShowDropIntentTranslucentBg
|
|
803
|
+
}
|
|
804
|
+
),
|
|
805
|
+
"show drop intent hint background"
|
|
806
|
+
]
|
|
807
|
+
}
|
|
808
|
+
),
|
|
809
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300", title: "Debug labels for drag source, drop target, and drop intent", children: [
|
|
810
|
+
/* @__PURE__ */ jsx(
|
|
811
|
+
StyledCheckbox,
|
|
812
|
+
{
|
|
813
|
+
checked: props.showDropIntentDebug,
|
|
814
|
+
onChange: props.setShowDropIntentDebug
|
|
815
|
+
}
|
|
816
|
+
),
|
|
817
|
+
"show drop intent debug labels"
|
|
818
|
+
] })
|
|
819
|
+
] }) : null
|
|
820
|
+
] });
|
|
821
|
+
const hitZoneOverlaysGroup = /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
822
|
+
/* @__PURE__ */ jsx(
|
|
823
|
+
GroupToggleHeader,
|
|
824
|
+
{
|
|
825
|
+
label: "hit-zone overlays",
|
|
826
|
+
enabled: props.hitZoneOverlaysEnabled,
|
|
827
|
+
onEnabledChange: props.setHitZoneOverlaysEnabled,
|
|
828
|
+
sticky: props.hitZoneOverlaysSticky,
|
|
829
|
+
onStickyChange: props.setHitZoneOverlaysSticky,
|
|
830
|
+
ariaLabel: "hit-zone overlays group",
|
|
831
|
+
offTitle: "Disable the persistent pane hit-zone debug overlay (center swap / edge split zones) and collapse the controls. Per-control states (alpha, subject colors) are preserved for re-enabling. Transient geometry-adjustment previews still fire. Debugging aid: off by default.",
|
|
832
|
+
onTitle: "Enable the persistent pane hit-zone debug overlay across every pane and expand the controls (alpha, subject colors)."
|
|
833
|
+
}
|
|
834
|
+
),
|
|
835
|
+
props.hitZoneOverlaysEnabled ? /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
|
|
836
|
+
/* @__PURE__ */ jsxs(
|
|
837
|
+
"label",
|
|
838
|
+
{
|
|
839
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
840
|
+
title: "Always show center swap and edge split hit zones for every pane",
|
|
841
|
+
children: [
|
|
842
|
+
/* @__PURE__ */ jsx(
|
|
843
|
+
StyledCheckbox,
|
|
844
|
+
{
|
|
845
|
+
checked: props.showPaneHitZones,
|
|
846
|
+
onChange: props.setShowPaneHitZones
|
|
847
|
+
}
|
|
848
|
+
),
|
|
849
|
+
"show pane hit zones"
|
|
850
|
+
]
|
|
851
|
+
}
|
|
852
|
+
),
|
|
853
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
854
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
855
|
+
"pane hit-zone alpha: ",
|
|
856
|
+
props.paneHitZonesAlphaPercent,
|
|
857
|
+
"%"
|
|
858
|
+
] }),
|
|
859
|
+
/* @__PURE__ */ jsx(
|
|
860
|
+
RangeSlider,
|
|
861
|
+
{
|
|
862
|
+
min: 0,
|
|
863
|
+
max: 100,
|
|
864
|
+
step: 1,
|
|
865
|
+
value: props.paneHitZonesAlphaPercent,
|
|
866
|
+
onChange: props.setPaneHitZonesAlphaPercent,
|
|
867
|
+
title: "Hit-zone overlay transparency"
|
|
868
|
+
}
|
|
869
|
+
)
|
|
870
|
+
] }),
|
|
871
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
872
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: "hit-zone subject colors" }),
|
|
873
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
874
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
875
|
+
"left edge",
|
|
876
|
+
/* @__PURE__ */ jsx(
|
|
877
|
+
"input",
|
|
878
|
+
{
|
|
879
|
+
type: "color",
|
|
880
|
+
value: props.observabilityColors.hitZoneLeftColorHex,
|
|
881
|
+
onChange: (event) => updateObservabilityColor("hitZoneLeftColorHex", event.target.value),
|
|
882
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950"
|
|
883
|
+
}
|
|
884
|
+
)
|
|
885
|
+
] }),
|
|
886
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
887
|
+
"right edge",
|
|
888
|
+
/* @__PURE__ */ jsx(
|
|
889
|
+
"input",
|
|
890
|
+
{
|
|
891
|
+
type: "color",
|
|
892
|
+
value: props.observabilityColors.hitZoneRightColorHex,
|
|
893
|
+
onChange: (event) => updateObservabilityColor("hitZoneRightColorHex", event.target.value),
|
|
894
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950"
|
|
895
|
+
}
|
|
896
|
+
)
|
|
897
|
+
] }),
|
|
898
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
899
|
+
"top edge",
|
|
900
|
+
/* @__PURE__ */ jsx(
|
|
901
|
+
"input",
|
|
902
|
+
{
|
|
903
|
+
type: "color",
|
|
904
|
+
value: props.observabilityColors.hitZoneTopColorHex,
|
|
905
|
+
onChange: (event) => updateObservabilityColor("hitZoneTopColorHex", event.target.value),
|
|
906
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950"
|
|
907
|
+
}
|
|
908
|
+
)
|
|
909
|
+
] }),
|
|
910
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
911
|
+
"bottom edge",
|
|
912
|
+
/* @__PURE__ */ jsx(
|
|
913
|
+
"input",
|
|
914
|
+
{
|
|
915
|
+
type: "color",
|
|
916
|
+
value: props.observabilityColors.hitZoneBottomColorHex,
|
|
917
|
+
onChange: (event) => updateObservabilityColor("hitZoneBottomColorHex", event.target.value),
|
|
918
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950"
|
|
919
|
+
}
|
|
920
|
+
)
|
|
921
|
+
] }),
|
|
922
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
923
|
+
"center zone",
|
|
924
|
+
/* @__PURE__ */ jsx(
|
|
925
|
+
"input",
|
|
926
|
+
{
|
|
927
|
+
type: "color",
|
|
928
|
+
value: props.observabilityColors.hitZoneCenterColorHex,
|
|
929
|
+
onChange: (event) => updateObservabilityColor("hitZoneCenterColorHex", event.target.value),
|
|
930
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950"
|
|
931
|
+
}
|
|
932
|
+
)
|
|
933
|
+
] }),
|
|
934
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
935
|
+
"blocked zone",
|
|
936
|
+
/* @__PURE__ */ jsx(
|
|
937
|
+
"input",
|
|
938
|
+
{
|
|
939
|
+
type: "color",
|
|
940
|
+
value: props.observabilityColors.hitZoneBlockedColorHex,
|
|
941
|
+
onChange: (event) => updateObservabilityColor("hitZoneBlockedColorHex", event.target.value),
|
|
942
|
+
className: "h-7 w-12 shrink-0 rounded border border-white/15 bg-slate-950"
|
|
943
|
+
}
|
|
944
|
+
)
|
|
945
|
+
] })
|
|
946
|
+
] })
|
|
947
|
+
] })
|
|
948
|
+
] }) : null
|
|
949
|
+
] });
|
|
950
|
+
const hitZoneGeometryGroup = /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
951
|
+
/* @__PURE__ */ jsx(
|
|
952
|
+
GroupToggleHeader,
|
|
953
|
+
{
|
|
954
|
+
label: "hit-zone geometry",
|
|
955
|
+
enabled: props.hitZoneGeometryEnabled,
|
|
956
|
+
onEnabledChange: props.setHitZoneGeometryEnabled,
|
|
957
|
+
sticky: props.hitZoneGeometrySticky,
|
|
958
|
+
onStickyChange: props.setHitZoneGeometrySticky,
|
|
959
|
+
ariaLabel: "hit-zone geometry group",
|
|
960
|
+
offTitle: "Collapse the drop hit-zone geometry knobs (center swap fraction, center floor, hysteresis). The current geometry values stay live and keep driving zone resolution; only the controls are hidden. Advanced tuning: off by default.",
|
|
961
|
+
onTitle: "Expand the drop hit-zone geometry knobs (center swap fraction, center floor, hysteresis). Adjusting any knob briefly previews the resolved zones across EVERY pane."
|
|
962
|
+
}
|
|
963
|
+
),
|
|
964
|
+
props.hitZoneGeometryEnabled ? /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2", children: [
|
|
965
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
966
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
967
|
+
"center swap X: ",
|
|
968
|
+
Math.round(props.interactionCapabilities.dropHitZoneGeometry.centerRatioX * 100),
|
|
969
|
+
"% (edge band ",
|
|
970
|
+
Math.round((1 - props.interactionCapabilities.dropHitZoneGeometry.centerRatioX) / 2 * 100),
|
|
971
|
+
"%)"
|
|
972
|
+
] }),
|
|
973
|
+
/* @__PURE__ */ jsx(
|
|
974
|
+
RangeSlider,
|
|
975
|
+
{
|
|
976
|
+
min: 5,
|
|
977
|
+
max: 95,
|
|
978
|
+
step: 1,
|
|
979
|
+
value: Math.round(props.interactionCapabilities.dropHitZoneGeometry.centerRatioX * 100),
|
|
980
|
+
onChange: (value) => props.setInteractionCapabilities(
|
|
981
|
+
(previous) => ({
|
|
982
|
+
...previous,
|
|
983
|
+
dropHitZoneGeometry: {
|
|
984
|
+
...previous.dropHitZoneGeometry,
|
|
985
|
+
// X axis drives the left/right swap-zone boundary; keep the
|
|
986
|
+
// representative `centerRatio` synced to X for the telemetry readouts.
|
|
987
|
+
centerRatio: value / 100,
|
|
988
|
+
centerRatioX: value / 100
|
|
989
|
+
}
|
|
990
|
+
})
|
|
991
|
+
),
|
|
992
|
+
title: "HORIZONTAL fraction of the pane spanned by the central SWAP rectangle (drives the left/right swap-zone boundary). Independent of the vertical axis so a non-square pane can carry an axis-specific swap-zone proportion."
|
|
993
|
+
}
|
|
994
|
+
)
|
|
995
|
+
] }),
|
|
996
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
997
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
998
|
+
"center swap Y: ",
|
|
999
|
+
Math.round(props.interactionCapabilities.dropHitZoneGeometry.centerRatioY * 100),
|
|
1000
|
+
"% (edge band ",
|
|
1001
|
+
Math.round((1 - props.interactionCapabilities.dropHitZoneGeometry.centerRatioY) / 2 * 100),
|
|
1002
|
+
"%)"
|
|
1003
|
+
] }),
|
|
1004
|
+
/* @__PURE__ */ jsx(
|
|
1005
|
+
RangeSlider,
|
|
1006
|
+
{
|
|
1007
|
+
min: 5,
|
|
1008
|
+
max: 95,
|
|
1009
|
+
step: 1,
|
|
1010
|
+
value: Math.round(props.interactionCapabilities.dropHitZoneGeometry.centerRatioY * 100),
|
|
1011
|
+
onChange: (value) => props.setInteractionCapabilities(
|
|
1012
|
+
(previous) => ({
|
|
1013
|
+
...previous,
|
|
1014
|
+
dropHitZoneGeometry: {
|
|
1015
|
+
...previous.dropHitZoneGeometry,
|
|
1016
|
+
centerRatioY: value / 100
|
|
1017
|
+
}
|
|
1018
|
+
})
|
|
1019
|
+
),
|
|
1020
|
+
title: "VERTICAL fraction of the pane spanned by the central SWAP rectangle (drives the top/bottom swap-zone boundary). Independent of the horizontal axis (per-axis centerRatio)."
|
|
1021
|
+
}
|
|
1022
|
+
)
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
1025
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1026
|
+
"center floor: ",
|
|
1027
|
+
props.interactionCapabilities.dropHitZoneGeometry.centerMinPx,
|
|
1028
|
+
"px"
|
|
1029
|
+
] }),
|
|
1030
|
+
/* @__PURE__ */ jsx(
|
|
1031
|
+
RangeSlider,
|
|
1032
|
+
{
|
|
1033
|
+
min: 0,
|
|
1034
|
+
max: 80,
|
|
1035
|
+
step: 1,
|
|
1036
|
+
value: props.interactionCapabilities.dropHitZoneGeometry.centerMinPx,
|
|
1037
|
+
onChange: (value) => props.setInteractionCapabilities(
|
|
1038
|
+
(previous) => ({
|
|
1039
|
+
...previous,
|
|
1040
|
+
dropHitZoneGeometry: {
|
|
1041
|
+
...previous.dropHitZoneGeometry,
|
|
1042
|
+
centerMinPx: value
|
|
1043
|
+
}
|
|
1044
|
+
})
|
|
1045
|
+
),
|
|
1046
|
+
title: "Floor (CSS px) for the center rectangle extent so tiny panes keep a usable swap target when width * centerRatio would collapse it."
|
|
1047
|
+
}
|
|
1048
|
+
)
|
|
1049
|
+
] }),
|
|
1050
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/70 p-2", children: [
|
|
1051
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1052
|
+
"hysteresis: ",
|
|
1053
|
+
props.interactionCapabilities.dropHitZoneGeometry.hysteresisPx,
|
|
1054
|
+
"px"
|
|
1055
|
+
] }),
|
|
1056
|
+
/* @__PURE__ */ jsx(
|
|
1057
|
+
RangeSlider,
|
|
1058
|
+
{
|
|
1059
|
+
min: 0,
|
|
1060
|
+
max: 24,
|
|
1061
|
+
step: 1,
|
|
1062
|
+
value: props.interactionCapabilities.dropHitZoneGeometry.hysteresisPx,
|
|
1063
|
+
onChange: (value) => props.setInteractionCapabilities(
|
|
1064
|
+
(previous) => ({
|
|
1065
|
+
...previous,
|
|
1066
|
+
dropHitZoneGeometry: {
|
|
1067
|
+
...previous.dropHitZoneGeometry,
|
|
1068
|
+
hysteresisPx: value
|
|
1069
|
+
}
|
|
1070
|
+
})
|
|
1071
|
+
),
|
|
1072
|
+
title: "Boundary stickiness (pane-local CSS px): once the cursor is in a zone it must cross the boundary by this much before the classification switches. 0 disables hysteresis."
|
|
1073
|
+
}
|
|
1074
|
+
)
|
|
1075
|
+
] })
|
|
1076
|
+
] }) : null
|
|
1077
|
+
] });
|
|
1078
|
+
const animationGroup = /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1079
|
+
/* @__PURE__ */ jsx(
|
|
1080
|
+
GroupToggleHeader,
|
|
1081
|
+
{
|
|
1082
|
+
label: "animation",
|
|
1083
|
+
enabled: props.animationControlsEnabled,
|
|
1084
|
+
onEnabledChange: props.setAnimationControlsEnabled,
|
|
1085
|
+
sticky: props.animationControlsSticky,
|
|
1086
|
+
onStickyChange: props.setAnimationControlsSticky,
|
|
1087
|
+
ariaLabel: "animation group",
|
|
1088
|
+
offTitle: "Disable the live-drag motion choreography (split transit/reflow speeds, swap bounce, ghost pickup-scale, coherent non-intersecting transit) and collapse the controls. Placement becomes instant; per-control values are preserved for re-enabling.",
|
|
1089
|
+
onTitle: "Enable the live-drag motion choreography and expand the controls (link toggle, transit + reflow speeds, swap bounce magnitude, ghost pickup scale, coherent non-intersecting transit)."
|
|
1090
|
+
}
|
|
1091
|
+
),
|
|
1092
|
+
props.animationControlsEnabled ? /* @__PURE__ */ jsxs("div", { className: "mt-2 space-y-2", children: [
|
|
1093
|
+
prefersReducedMotion ? /* @__PURE__ */ jsx("div", { className: "rounded border border-amber-400/40 bg-amber-500/10 px-2 py-1 font-mono text-[9px] uppercase leading-relaxed tracking-[0.12em] text-amber-200", children: "reduced motion active \u2014 drag choreography is suppressed; these knobs have no visible effect until the OS setting is cleared" }) : null,
|
|
1094
|
+
/* @__PURE__ */ jsxs(
|
|
1095
|
+
"label",
|
|
1096
|
+
{
|
|
1097
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1098
|
+
title: "Link the ghost transit speed and the survivor reflow speed. Linked (default): one slider drives both at equal (parity) timing \u2014 the only regime where coherent non-intersecting transit is geometrically valid. Unlinked: two independent sliders; coherent transit is gated off while speeds can diverge.",
|
|
1099
|
+
children: [
|
|
1100
|
+
/* @__PURE__ */ jsx(
|
|
1101
|
+
StyledCheckbox,
|
|
1102
|
+
{
|
|
1103
|
+
checked: props.animationSpeedLinked,
|
|
1104
|
+
onChange: (checked) => {
|
|
1105
|
+
props.setAnimationSpeedLinked(checked);
|
|
1106
|
+
if (checked) {
|
|
1107
|
+
props.setSurvivorReflowSpeedPercent(props.ghostTransitSpeedPercent);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
),
|
|
1112
|
+
"link transit + reflow speed"
|
|
1113
|
+
]
|
|
1114
|
+
}
|
|
1115
|
+
),
|
|
1116
|
+
props.animationSpeedLinked ? /* @__PURE__ */ jsxs("div", { className: "pl-0.5", children: [
|
|
1117
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1118
|
+
"drag animation speed: ",
|
|
1119
|
+
props.ghostTransitSpeedPercent,
|
|
1120
|
+
"% (",
|
|
1121
|
+
resolvedGhostTransitDurationMs,
|
|
1122
|
+
"ms transit + reflow)"
|
|
1123
|
+
] }),
|
|
1124
|
+
/* @__PURE__ */ jsx(
|
|
1125
|
+
RangeSlider,
|
|
1126
|
+
{
|
|
1127
|
+
min: DRAG_ANIMATION_SPEED_MIN_PERCENT,
|
|
1128
|
+
max: DRAG_ANIMATION_SPEED_MAX_PERCENT,
|
|
1129
|
+
step: 5,
|
|
1130
|
+
value: props.ghostTransitSpeedPercent,
|
|
1131
|
+
onChange: (value) => {
|
|
1132
|
+
props.setGhostTransitSpeedPercent(value);
|
|
1133
|
+
props.setSurvivorReflowSpeedPercent(value);
|
|
1134
|
+
},
|
|
1135
|
+
title: "Linked speed for both the ghost transit (hop) and the survivor reflow, scaled from the 170ms baseline (100% = default). Slowest 10% \u2248 1700ms."
|
|
1136
|
+
}
|
|
1137
|
+
)
|
|
1138
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1139
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-0.5", children: [
|
|
1140
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1141
|
+
"ghost transit speed: ",
|
|
1142
|
+
props.ghostTransitSpeedPercent,
|
|
1143
|
+
"% (",
|
|
1144
|
+
resolvedGhostTransitDurationMs,
|
|
1145
|
+
"ms hop)"
|
|
1146
|
+
] }),
|
|
1147
|
+
/* @__PURE__ */ jsx(
|
|
1148
|
+
RangeSlider,
|
|
1149
|
+
{
|
|
1150
|
+
min: DRAG_ANIMATION_SPEED_MIN_PERCENT,
|
|
1151
|
+
max: DRAG_ANIMATION_SPEED_MAX_PERCENT,
|
|
1152
|
+
step: 5,
|
|
1153
|
+
value: props.ghostTransitSpeedPercent,
|
|
1154
|
+
onChange: props.setGhostTransitSpeedPercent,
|
|
1155
|
+
title: "Speed of the dragged ghost's hop-in/out + pickup entrance, scaled from the 170ms baseline (100% = default)."
|
|
1156
|
+
}
|
|
1157
|
+
)
|
|
1158
|
+
] }),
|
|
1159
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-0.5", children: [
|
|
1160
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1161
|
+
"survivor reflow speed: ",
|
|
1162
|
+
props.survivorReflowSpeedPercent,
|
|
1163
|
+
"% (",
|
|
1164
|
+
resolvedSurvivorReflowDurationMs,
|
|
1165
|
+
"ms reflow)"
|
|
1166
|
+
] }),
|
|
1167
|
+
/* @__PURE__ */ jsx(
|
|
1168
|
+
RangeSlider,
|
|
1169
|
+
{
|
|
1170
|
+
min: DRAG_ANIMATION_SPEED_MIN_PERCENT,
|
|
1171
|
+
max: DRAG_ANIMATION_SPEED_MAX_PERCENT,
|
|
1172
|
+
step: 5,
|
|
1173
|
+
value: props.survivorReflowSpeedPercent,
|
|
1174
|
+
onChange: props.setSurvivorReflowSpeedPercent,
|
|
1175
|
+
title: "Speed of the affected (survivor / displaced) panes' reflow transform, scaled from the 170ms baseline (100% = default)."
|
|
1176
|
+
}
|
|
1177
|
+
)
|
|
1178
|
+
] })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-0.5", children: [
|
|
1181
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1182
|
+
"swap bounce magnitude: ",
|
|
1183
|
+
props.swapBounceMagnitudePercent,
|
|
1184
|
+
"% ",
|
|
1185
|
+
props.swapBounceMagnitudePercent === 0 ? "(no overshoot)" : "(overshoot)"
|
|
1186
|
+
] }),
|
|
1187
|
+
/* @__PURE__ */ jsx(
|
|
1188
|
+
RangeSlider,
|
|
1189
|
+
{
|
|
1190
|
+
min: SWAP_BOUNCE_MIN_PERCENT,
|
|
1191
|
+
max: SWAP_BOUNCE_MAX_PERCENT,
|
|
1192
|
+
step: 5,
|
|
1193
|
+
value: props.swapBounceMagnitudePercent,
|
|
1194
|
+
onChange: props.setSwapBounceMagnitudePercent,
|
|
1195
|
+
title: "Landing overshoot amplitude for the ghost seated hop-in + survivor settle (easeOutBack). 0% = today's monotonic settle. Per-element, so it is valid under split (unlinked) speeds; inert while the coherent-transit dip owns the swap landing. Skipped under prefers-reduced-motion."
|
|
1196
|
+
}
|
|
1197
|
+
)
|
|
1198
|
+
] }),
|
|
1199
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-0.5", children: [
|
|
1200
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1201
|
+
"hop easing: ",
|
|
1202
|
+
DRAG_HOP_EASING_PRESETS.find((preset) => preset.value === props.dragHopEasing)?.label ?? "custom"
|
|
1203
|
+
] }),
|
|
1204
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "drag hop easing preset", children: DRAG_HOP_EASING_PRESETS.map((preset) => {
|
|
1205
|
+
const isActive = preset.value === props.dragHopEasing;
|
|
1206
|
+
return /* @__PURE__ */ jsx(
|
|
1207
|
+
"button",
|
|
1208
|
+
{
|
|
1209
|
+
type: "button",
|
|
1210
|
+
onClick: () => props.setDragHopEasing(preset.value),
|
|
1211
|
+
title: `Set the ghost hop + cursor transit timing function to ${preset.value} (the survivor reflow falls back to this curve).`,
|
|
1212
|
+
className: `rounded border px-2 py-1 font-mono text-[9px] uppercase tracking-[0.12em] transition-colors ${isActive ? "border-sky-400/70 bg-sky-500/20 text-sky-200" : "border-white/10 bg-slate-950/70 text-slate-400 hover:border-white/20 hover:text-slate-200"}`,
|
|
1213
|
+
children: preset.label
|
|
1214
|
+
},
|
|
1215
|
+
preset.id
|
|
1216
|
+
);
|
|
1217
|
+
}) })
|
|
1218
|
+
] }),
|
|
1219
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-0.5", children: [
|
|
1220
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1221
|
+
"ghost pickup scale: ",
|
|
1222
|
+
props.interactionCapabilities.ghostPickupScalePercent,
|
|
1223
|
+
"% (of source bbox)"
|
|
1224
|
+
] }),
|
|
1225
|
+
/* @__PURE__ */ jsx(
|
|
1226
|
+
RangeSlider,
|
|
1227
|
+
{
|
|
1228
|
+
min: GHOST_PICKUP_SCALE_MIN_PERCENT,
|
|
1229
|
+
max: GHOST_PICKUP_SCALE_MAX_PERCENT,
|
|
1230
|
+
step: 5,
|
|
1231
|
+
value: props.interactionCapabilities.ghostPickupScalePercent,
|
|
1232
|
+
onChange: (value) => props.setInteractionCapabilities(
|
|
1233
|
+
(previous) => ({
|
|
1234
|
+
...previous,
|
|
1235
|
+
ghostPickupScalePercent: value
|
|
1236
|
+
})
|
|
1237
|
+
),
|
|
1238
|
+
title: "Size the lifted ghost relative to the source pane's full bbox on drag start. <100% reads as lifted/shrunk; the ghost morphs from this size toward the resolved slot's bbox on hop-in. Skipped under prefers-reduced-motion."
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
] }),
|
|
1242
|
+
/* @__PURE__ */ jsxs(
|
|
1243
|
+
"label",
|
|
1244
|
+
{
|
|
1245
|
+
className: `flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] ${props.animationSpeedLinked ? "text-slate-300" : "text-slate-600"}`,
|
|
1246
|
+
title: "Coherent non-intersecting transit. On a SWAP the moving ghost and the displaced target dip toward ~70% mid-transit so their crossing paths never visually overlap, then scale back into place. Only valid at equal (parity) timing, so it is disabled while the transit + reflow speeds are unlinked.",
|
|
1247
|
+
children: [
|
|
1248
|
+
/* @__PURE__ */ jsx(
|
|
1249
|
+
StyledCheckbox,
|
|
1250
|
+
{
|
|
1251
|
+
checked: props.animationSpeedLinked && props.interactionCapabilities.coherentTransit,
|
|
1252
|
+
disabled: !props.animationSpeedLinked,
|
|
1253
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1254
|
+
(previous) => ({
|
|
1255
|
+
...previous,
|
|
1256
|
+
coherentTransit: checked
|
|
1257
|
+
})
|
|
1258
|
+
)
|
|
1259
|
+
}
|
|
1260
|
+
),
|
|
1261
|
+
"coherent non-intersecting transit"
|
|
1262
|
+
]
|
|
1263
|
+
}
|
|
1264
|
+
),
|
|
1265
|
+
props.animationSpeedLinked ? null : /* @__PURE__ */ jsx("div", { className: "pl-6 font-mono text-[9px] uppercase tracking-[0.12em] text-slate-500", children: "requires linked transit + reflow speed" }),
|
|
1266
|
+
/* @__PURE__ */ jsx(
|
|
1267
|
+
"button",
|
|
1268
|
+
{
|
|
1269
|
+
type: "button",
|
|
1270
|
+
onClick: () => {
|
|
1271
|
+
props.setAnimationSpeedLinked(ANIMATION_CONTROL_DEFAULTS.speedLinked);
|
|
1272
|
+
props.setGhostTransitSpeedPercent(ANIMATION_CONTROL_DEFAULTS.ghostTransitSpeedPercent);
|
|
1273
|
+
props.setSurvivorReflowSpeedPercent(ANIMATION_CONTROL_DEFAULTS.survivorReflowSpeedPercent);
|
|
1274
|
+
props.setSwapBounceMagnitudePercent(ANIMATION_CONTROL_DEFAULTS.swapBounceMagnitudePercent);
|
|
1275
|
+
props.setDragHopEasing(DEFAULT_DRAG_HOP_EASING);
|
|
1276
|
+
props.setInteractionCapabilities(
|
|
1277
|
+
(previous) => ({
|
|
1278
|
+
...previous,
|
|
1279
|
+
ghostPickupScalePercent: ANIMATION_CONTROL_DEFAULTS.ghostPickupScalePercent,
|
|
1280
|
+
coherentTransit: ANIMATION_CONTROL_DEFAULTS.coherentTransit
|
|
1281
|
+
})
|
|
1282
|
+
);
|
|
1283
|
+
},
|
|
1284
|
+
className: "mt-1 w-full rounded border border-white/15 bg-slate-900/80 px-2 py-1 font-mono text-[9px] uppercase tracking-[0.14em] text-slate-300 transition-colors hover:border-cyan-300/50 hover:text-cyan-100",
|
|
1285
|
+
title: "Restore the ANIMATION group to its defaults: linked speeds at 100%, swap bounce 30%, ghost pickup scale 90%, coherent transit on.",
|
|
1286
|
+
children: "reset group to defaults"
|
|
1287
|
+
}
|
|
1288
|
+
)
|
|
1289
|
+
] }) : null
|
|
1290
|
+
] });
|
|
1291
|
+
const isLiveStatusPinned = props.showLiveStatus && props.liveStatusSticky;
|
|
1292
|
+
const anyGroupPinned = isLiveStatusPinned || props.previewOverlaysSticky || props.subjectColorsSticky || props.dropIntentDebugSticky || props.hitZoneOverlaysSticky || props.hitZoneGeometrySticky || props.animationControlsSticky;
|
|
1293
|
+
return /* @__PURE__ */ jsxs(
|
|
1294
|
+
"aside",
|
|
1295
|
+
{
|
|
1296
|
+
className: "hypr-range-scope h-full min-h-0 shrink-0 overflow-hidden rounded-xl border border-cyan-100/20 bg-[linear-gradient(180deg,rgba(15,23,42,0.84),rgba(2,6,23,0.94))] p-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.05),0_0_22px_rgba(34,211,238,0.12)] backdrop-blur",
|
|
1297
|
+
style: {
|
|
1298
|
+
width: CONTROL_PANE_WIDTH_PX,
|
|
1299
|
+
minWidth: CONTROL_PANE_WIDTH_PX,
|
|
1300
|
+
maxWidth: CONTROL_PANE_WIDTH_PX
|
|
1301
|
+
},
|
|
1302
|
+
children: [
|
|
1303
|
+
/* @__PURE__ */ jsx("style", { children: RANGE_SLIDER_STYLE }),
|
|
1304
|
+
/* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 min-w-0 max-w-full flex-col overflow-hidden", children: [
|
|
1305
|
+
/* @__PURE__ */ jsx("section", { className: "shrink-0 min-w-0 max-w-full overflow-hidden rounded-lg border border-cyan-100/20 bg-slate-900/80 p-1.5 shadow-[inset_0_1px_0_rgba(255,255,255,0.05)]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1306
|
+
/* @__PURE__ */ jsx(
|
|
1307
|
+
"div",
|
|
1308
|
+
{
|
|
1309
|
+
className: "min-w-0 flex-1 truncate font-mono text-[10px] uppercase tracking-[0.13em] text-slate-400",
|
|
1310
|
+
title: "Show or hide the live status readout. Hidden by default so it is opt-in rather than always pinned to the top of the pane. Pin keeps it above the scroll area when shown.",
|
|
1311
|
+
children: "live status panel"
|
|
1312
|
+
}
|
|
1313
|
+
),
|
|
1314
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
1315
|
+
/* @__PURE__ */ jsx(
|
|
1316
|
+
StickyPinToggle,
|
|
1317
|
+
{
|
|
1318
|
+
sticky: props.liveStatusSticky,
|
|
1319
|
+
onStickyChange: props.setLiveStatusSticky,
|
|
1320
|
+
ariaLabel: "live status panel sticky"
|
|
1321
|
+
}
|
|
1322
|
+
),
|
|
1323
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", role: "group", "aria-label": "live status panel visibility", children: LIVE_STATUS_VISIBILITY_OPTIONS.map((option) => {
|
|
1324
|
+
const isActive = props.showLiveStatus === option.value;
|
|
1325
|
+
return /* @__PURE__ */ jsx(
|
|
1326
|
+
"button",
|
|
1327
|
+
{
|
|
1328
|
+
type: "button",
|
|
1329
|
+
"aria-pressed": isActive,
|
|
1330
|
+
title: option.title,
|
|
1331
|
+
onClick: () => props.setShowLiveStatus(option.value),
|
|
1332
|
+
className: `rounded border px-2 py-0.5 font-mono text-[10px] uppercase tracking-[0.1em] transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300/55 focus-visible:ring-offset-1 focus-visible:ring-offset-slate-950 motion-reduce:transition-none ${isActive ? "border-cyan-300/70 bg-cyan-500/20 text-cyan-100" : "border-white/10 bg-slate-950 text-slate-400 hover:border-white/25"}`,
|
|
1333
|
+
children: option.label
|
|
1334
|
+
},
|
|
1335
|
+
`live-status-${option.label}`
|
|
1336
|
+
);
|
|
1337
|
+
}) })
|
|
1338
|
+
] })
|
|
1339
|
+
] }) }),
|
|
1340
|
+
anyGroupPinned ? /* @__PURE__ */ jsxs("div", { className: "mt-3 shrink-0 min-w-0 max-w-full space-y-3 overflow-hidden", children: [
|
|
1341
|
+
isLiveStatusPinned ? liveStatusReadout : null,
|
|
1342
|
+
props.animationControlsSticky ? animationGroup : null,
|
|
1343
|
+
props.previewOverlaysSticky ? previewOverlaysGroup : null,
|
|
1344
|
+
props.subjectColorsSticky ? subjectColorsGroup : null,
|
|
1345
|
+
props.dropIntentDebugSticky ? dropIntentDebugGroup : null,
|
|
1346
|
+
props.hitZoneOverlaysSticky ? hitZoneOverlaysGroup : null,
|
|
1347
|
+
props.hitZoneGeometrySticky ? hitZoneGeometryGroup : null
|
|
1348
|
+
] }) : null,
|
|
1349
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 min-h-0 min-w-0 max-w-full flex-1 space-y-3 overflow-y-auto overflow-x-hidden pr-1", children: [
|
|
1350
|
+
props.showLiveStatus && !props.liveStatusSticky ? liveStatusReadout : null,
|
|
1351
|
+
props.animationControlsSticky ? null : animationGroup,
|
|
1352
|
+
/* @__PURE__ */ jsxs("section", { className: "min-w-0 max-w-full space-y-3 overflow-hidden rounded-lg border border-cyan-100/15 bg-slate-900/65 p-2 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]", children: [
|
|
1353
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[10px] uppercase tracking-[0.14em] text-cyan-200", children: "primary controls" }),
|
|
1354
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded-lg border border-cyan-200/25 bg-slate-950/65 p-2 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]", children: [
|
|
1355
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "interaction capabilities" }),
|
|
1356
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1357
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1358
|
+
/* @__PURE__ */ jsx(
|
|
1359
|
+
"div",
|
|
1360
|
+
{
|
|
1361
|
+
className: "mb-1 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400",
|
|
1362
|
+
title: "Divider resize axis: horizontal = width dividers (side-by-side), vertical = height dividers (stacked)",
|
|
1363
|
+
children: "resize dividers"
|
|
1364
|
+
}
|
|
1365
|
+
),
|
|
1366
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-1", role: "group", "aria-label": "resize capability", children: RESIZE_CAPABILITY_OPTIONS.map((option) => {
|
|
1367
|
+
const isActive = props.interactionCapabilities.resize === option.value;
|
|
1368
|
+
return /* @__PURE__ */ jsx(
|
|
1369
|
+
"button",
|
|
1370
|
+
{
|
|
1371
|
+
type: "button",
|
|
1372
|
+
"aria-pressed": isActive,
|
|
1373
|
+
title: option.title,
|
|
1374
|
+
onClick: () => props.setInteractionCapabilities(
|
|
1375
|
+
(previous) => ({
|
|
1376
|
+
...previous,
|
|
1377
|
+
resize: option.value
|
|
1378
|
+
})
|
|
1379
|
+
),
|
|
1380
|
+
className: `rounded border px-2 py-1 font-mono text-[10px] uppercase tracking-[0.1em] transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300/55 focus-visible:ring-offset-1 focus-visible:ring-offset-slate-950 motion-reduce:transition-none ${isActive ? "border-cyan-300/70 bg-cyan-500/20 text-cyan-100" : "border-white/10 bg-slate-950 text-slate-400 hover:border-white/25"}`,
|
|
1381
|
+
children: option.label
|
|
1382
|
+
},
|
|
1383
|
+
`resize-${option.value}`
|
|
1384
|
+
);
|
|
1385
|
+
}) })
|
|
1386
|
+
] }),
|
|
1387
|
+
/* @__PURE__ */ jsxs(
|
|
1388
|
+
"label",
|
|
1389
|
+
{
|
|
1390
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1391
|
+
title: "Render split-divider handle chrome between panes. Off hides separator paint/hover affordance but keeps divider hit-targets and resize capability behavior.",
|
|
1392
|
+
children: [
|
|
1393
|
+
/* @__PURE__ */ jsx(
|
|
1394
|
+
StyledCheckbox,
|
|
1395
|
+
{
|
|
1396
|
+
checked: props.interactionCapabilities.resizeHandlesVisible,
|
|
1397
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1398
|
+
(previous) => ({
|
|
1399
|
+
...previous,
|
|
1400
|
+
resizeHandlesVisible: checked
|
|
1401
|
+
})
|
|
1402
|
+
)
|
|
1403
|
+
}
|
|
1404
|
+
),
|
|
1405
|
+
"show resize handles"
|
|
1406
|
+
]
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
/* @__PURE__ */ jsxs(
|
|
1410
|
+
"label",
|
|
1411
|
+
{
|
|
1412
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1413
|
+
title: "Live-drag slot hop-in. On (default): the single ghost hops INTO and FILLS the resolved slot as the single instance \u2014 no separate empty reservation lingers. Off: the ghost free-follows the cursor and the in-tree content-less reservation slot stays shown (reservation-plus-ghost duality).",
|
|
1414
|
+
children: [
|
|
1415
|
+
/* @__PURE__ */ jsx(
|
|
1416
|
+
StyledCheckbox,
|
|
1417
|
+
{
|
|
1418
|
+
disabled: !props.interactionCapabilities.rearrange || props.interactionCapabilities.dragMode !== "live",
|
|
1419
|
+
checked: props.interactionCapabilities.slotHopInEnabled,
|
|
1420
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1421
|
+
(previous) => ({
|
|
1422
|
+
...previous,
|
|
1423
|
+
slotHopInEnabled: checked
|
|
1424
|
+
})
|
|
1425
|
+
)
|
|
1426
|
+
}
|
|
1427
|
+
),
|
|
1428
|
+
"slot hop-in"
|
|
1429
|
+
]
|
|
1430
|
+
}
|
|
1431
|
+
),
|
|
1432
|
+
/* @__PURE__ */ jsxs(
|
|
1433
|
+
"label",
|
|
1434
|
+
{
|
|
1435
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1436
|
+
title: "Drag-to-rearrange (move / swap / edge-insert). When off, panes are not draggable and no drop overlays activate.",
|
|
1437
|
+
children: [
|
|
1438
|
+
/* @__PURE__ */ jsx(
|
|
1439
|
+
StyledCheckbox,
|
|
1440
|
+
{
|
|
1441
|
+
checked: props.interactionCapabilities.rearrange,
|
|
1442
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1443
|
+
(previous) => ({
|
|
1444
|
+
...previous,
|
|
1445
|
+
rearrange: checked
|
|
1446
|
+
})
|
|
1447
|
+
)
|
|
1448
|
+
}
|
|
1449
|
+
),
|
|
1450
|
+
"drag rearrange"
|
|
1451
|
+
]
|
|
1452
|
+
}
|
|
1453
|
+
),
|
|
1454
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-5", children: [
|
|
1455
|
+
/* @__PURE__ */ jsx(
|
|
1456
|
+
"div",
|
|
1457
|
+
{
|
|
1458
|
+
className: "mb-1 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400",
|
|
1459
|
+
title: "Drag feedback mode: preview = non-committing projected overlays; live = Hyprland detach (source detached on pickup, frozen tree, ghost follows cursor, commit on release)",
|
|
1460
|
+
children: "drag mode"
|
|
1461
|
+
}
|
|
1462
|
+
),
|
|
1463
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", role: "group", "aria-label": "drag mode", children: DRAG_MODE_OPTIONS.map((option) => {
|
|
1464
|
+
const isActive = props.interactionCapabilities.dragMode === option.value;
|
|
1465
|
+
return /* @__PURE__ */ jsx(
|
|
1466
|
+
"button",
|
|
1467
|
+
{
|
|
1468
|
+
type: "button",
|
|
1469
|
+
"aria-pressed": isActive,
|
|
1470
|
+
disabled: !props.interactionCapabilities.rearrange,
|
|
1471
|
+
title: option.title,
|
|
1472
|
+
onClick: () => props.setInteractionCapabilities(
|
|
1473
|
+
(previous) => ({
|
|
1474
|
+
...previous,
|
|
1475
|
+
dragMode: option.value
|
|
1476
|
+
})
|
|
1477
|
+
),
|
|
1478
|
+
className: `rounded border px-2 py-1 font-mono text-[10px] uppercase tracking-[0.1em] transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300/55 focus-visible:ring-offset-1 focus-visible:ring-offset-slate-950 motion-reduce:transition-none ${isActive ? "border-cyan-300/70 bg-cyan-500/20 text-cyan-100" : "border-white/10 bg-slate-950 text-slate-400 hover:border-white/25"} ${props.interactionCapabilities.rearrange ? "" : "opacity-40"}`,
|
|
1479
|
+
children: option.label
|
|
1480
|
+
},
|
|
1481
|
+
`drag-mode-${option.value}`
|
|
1482
|
+
);
|
|
1483
|
+
}) })
|
|
1484
|
+
] }),
|
|
1485
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-5", children: [
|
|
1486
|
+
/* @__PURE__ */ jsx(
|
|
1487
|
+
"div",
|
|
1488
|
+
{
|
|
1489
|
+
className: "mb-1 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400",
|
|
1490
|
+
title: "Live-drag slot re-resolution after the single ghost hops INTO and FILLS a slot: delta-responsive (default) re-aims once the cursor moves past the 24px delta; zone-exit-hold pins the seated slot until the cursor leaves the seated pane.",
|
|
1491
|
+
children: "slot commitment"
|
|
1492
|
+
}
|
|
1493
|
+
),
|
|
1494
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", role: "group", "aria-label": "slot commitment", children: SLOT_COMMITMENT_OPTIONS.map((option) => {
|
|
1495
|
+
const isActive = props.interactionCapabilities.slotCommitment.mode === option.value;
|
|
1496
|
+
const isDisabled = !props.interactionCapabilities.rearrange || props.interactionCapabilities.dragMode !== "live";
|
|
1497
|
+
return /* @__PURE__ */ jsx(
|
|
1498
|
+
"button",
|
|
1499
|
+
{
|
|
1500
|
+
type: "button",
|
|
1501
|
+
"aria-pressed": isActive,
|
|
1502
|
+
disabled: isDisabled,
|
|
1503
|
+
title: option.title,
|
|
1504
|
+
onClick: () => props.setInteractionCapabilities(
|
|
1505
|
+
(previous) => ({
|
|
1506
|
+
...previous,
|
|
1507
|
+
slotCommitment: { ...previous.slotCommitment, mode: option.value }
|
|
1508
|
+
})
|
|
1509
|
+
),
|
|
1510
|
+
className: `rounded border px-2 py-1 font-mono text-[10px] uppercase tracking-[0.1em] transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cyan-300/55 focus-visible:ring-offset-1 focus-visible:ring-offset-slate-950 motion-reduce:transition-none ${isActive ? "border-cyan-300/70 bg-cyan-500/20 text-cyan-100" : "border-white/10 bg-slate-950 text-slate-400 hover:border-white/25"} ${isDisabled ? "opacity-40" : ""}`,
|
|
1511
|
+
children: option.label
|
|
1512
|
+
},
|
|
1513
|
+
`slot-commitment-${option.value}`
|
|
1514
|
+
);
|
|
1515
|
+
}) })
|
|
1516
|
+
] }),
|
|
1517
|
+
/* @__PURE__ */ jsxs(
|
|
1518
|
+
"label",
|
|
1519
|
+
{
|
|
1520
|
+
className: "flex items-center gap-2 pl-5 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1521
|
+
title: "Allow touch pointers to start a drag. When off, touch is reserved for tap/scroll and only mouse/pen can drag. The drag FSM runs on Pointer Events, so touch shares the same pickup/drop machinery as mouse.",
|
|
1522
|
+
children: [
|
|
1523
|
+
/* @__PURE__ */ jsx(
|
|
1524
|
+
StyledCheckbox,
|
|
1525
|
+
{
|
|
1526
|
+
disabled: !props.interactionCapabilities.rearrange,
|
|
1527
|
+
checked: props.interactionCapabilities.touchDrag.enable,
|
|
1528
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1529
|
+
(previous) => ({
|
|
1530
|
+
...previous,
|
|
1531
|
+
touchDrag: { ...previous.touchDrag, enable: checked }
|
|
1532
|
+
})
|
|
1533
|
+
)
|
|
1534
|
+
}
|
|
1535
|
+
),
|
|
1536
|
+
"touch drag"
|
|
1537
|
+
]
|
|
1538
|
+
}
|
|
1539
|
+
),
|
|
1540
|
+
/* @__PURE__ */ jsxs("div", { className: "pl-5", children: [
|
|
1541
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-1 block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: [
|
|
1542
|
+
"touch long-press: ",
|
|
1543
|
+
props.interactionCapabilities.touchDrag.longPressMs,
|
|
1544
|
+
"ms"
|
|
1545
|
+
] }),
|
|
1546
|
+
/* @__PURE__ */ jsx(
|
|
1547
|
+
RangeSlider,
|
|
1548
|
+
{
|
|
1549
|
+
min: 0,
|
|
1550
|
+
max: 600,
|
|
1551
|
+
step: 10,
|
|
1552
|
+
value: props.interactionCapabilities.touchDrag.longPressMs,
|
|
1553
|
+
disabled: !props.interactionCapabilities.rearrange || !props.interactionCapabilities.touchDrag.enable,
|
|
1554
|
+
onChange: (value) => props.setInteractionCapabilities(
|
|
1555
|
+
(previous) => ({
|
|
1556
|
+
...previous,
|
|
1557
|
+
touchDrag: { ...previous.touchDrag, longPressMs: value }
|
|
1558
|
+
})
|
|
1559
|
+
),
|
|
1560
|
+
title: "How long a finger must be held before a touch press becomes a drag (the tap/scroll-vs-drag disambiguator). A pre-long-press scroll-axis flick releases to the page. Mouse/pen ignore this delay (immediate threshold pickup). 0 = a held touch picks up at once.",
|
|
1561
|
+
ariaLabel: "touch long-press delay in milliseconds"
|
|
1562
|
+
}
|
|
1563
|
+
)
|
|
1564
|
+
] }),
|
|
1565
|
+
/* @__PURE__ */ jsxs(
|
|
1566
|
+
"label",
|
|
1567
|
+
{
|
|
1568
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1569
|
+
title: "Pane focus selection. When off, clicking / focusing a pane does not select it.",
|
|
1570
|
+
children: [
|
|
1571
|
+
/* @__PURE__ */ jsx(
|
|
1572
|
+
StyledCheckbox,
|
|
1573
|
+
{
|
|
1574
|
+
checked: props.interactionCapabilities.focus,
|
|
1575
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1576
|
+
(previous) => ({
|
|
1577
|
+
...previous,
|
|
1578
|
+
focus: checked
|
|
1579
|
+
})
|
|
1580
|
+
)
|
|
1581
|
+
}
|
|
1582
|
+
),
|
|
1583
|
+
"pane focus selection"
|
|
1584
|
+
]
|
|
1585
|
+
}
|
|
1586
|
+
),
|
|
1587
|
+
/* @__PURE__ */ jsxs(
|
|
1588
|
+
"label",
|
|
1589
|
+
{
|
|
1590
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1591
|
+
title: "Maximize-to-viewport. Adds a per-pane maximize/restore header button and binds Alt+Enter / Esc. Non-destructive render-mode (layout tree untouched).",
|
|
1592
|
+
children: [
|
|
1593
|
+
/* @__PURE__ */ jsx(
|
|
1594
|
+
StyledCheckbox,
|
|
1595
|
+
{
|
|
1596
|
+
checked: props.interactionCapabilities.maximize.enable,
|
|
1597
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1598
|
+
(previous) => ({
|
|
1599
|
+
...previous,
|
|
1600
|
+
maximize: { ...previous.maximize, enable: checked }
|
|
1601
|
+
})
|
|
1602
|
+
)
|
|
1603
|
+
}
|
|
1604
|
+
),
|
|
1605
|
+
"maximize (Alt+Enter / Esc)"
|
|
1606
|
+
]
|
|
1607
|
+
}
|
|
1608
|
+
),
|
|
1609
|
+
/* @__PURE__ */ jsxs(
|
|
1610
|
+
"label",
|
|
1611
|
+
{
|
|
1612
|
+
className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1613
|
+
title: "Tab-like pane switching. Binds Alt+[ / Alt+] cycle and Alt+1..9 jump. When maximized, switching also switches which pane is maximized.",
|
|
1614
|
+
children: [
|
|
1615
|
+
/* @__PURE__ */ jsx(
|
|
1616
|
+
StyledCheckbox,
|
|
1617
|
+
{
|
|
1618
|
+
checked: props.interactionCapabilities.paneSwitching.enable,
|
|
1619
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1620
|
+
(previous) => ({
|
|
1621
|
+
...previous,
|
|
1622
|
+
paneSwitching: { ...previous.paneSwitching, enable: checked }
|
|
1623
|
+
})
|
|
1624
|
+
)
|
|
1625
|
+
}
|
|
1626
|
+
),
|
|
1627
|
+
"pane switching (Alt+[ / Alt+] / Alt+1..9)"
|
|
1628
|
+
]
|
|
1629
|
+
}
|
|
1630
|
+
),
|
|
1631
|
+
/* @__PURE__ */ jsxs(
|
|
1632
|
+
"label",
|
|
1633
|
+
{
|
|
1634
|
+
className: "flex items-center gap-2 pl-5 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1635
|
+
title: "Render the tab strip above the tiling region. Cycle/jump shortcuts still work when off.",
|
|
1636
|
+
children: [
|
|
1637
|
+
/* @__PURE__ */ jsx(
|
|
1638
|
+
StyledCheckbox,
|
|
1639
|
+
{
|
|
1640
|
+
disabled: !props.interactionCapabilities.paneSwitching.enable,
|
|
1641
|
+
checked: props.interactionCapabilities.paneSwitching.showTabStrip,
|
|
1642
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1643
|
+
(previous) => ({
|
|
1644
|
+
...previous,
|
|
1645
|
+
paneSwitching: { ...previous.paneSwitching, showTabStrip: checked }
|
|
1646
|
+
})
|
|
1647
|
+
)
|
|
1648
|
+
}
|
|
1649
|
+
),
|
|
1650
|
+
"tab strip visible"
|
|
1651
|
+
]
|
|
1652
|
+
}
|
|
1653
|
+
),
|
|
1654
|
+
/* @__PURE__ */ jsxs(
|
|
1655
|
+
"label",
|
|
1656
|
+
{
|
|
1657
|
+
className: "flex items-center gap-2 pl-5 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300",
|
|
1658
|
+
title: "macOS Cmd+Tab-style visual switcher overlay while cycling panes with Alt held. Release Alt to commit, Esc to cancel. Cycle/jump shortcuts still work when off (they activate immediately).",
|
|
1659
|
+
children: [
|
|
1660
|
+
/* @__PURE__ */ jsx(
|
|
1661
|
+
StyledCheckbox,
|
|
1662
|
+
{
|
|
1663
|
+
disabled: !props.interactionCapabilities.paneSwitching.enable,
|
|
1664
|
+
checked: props.interactionCapabilities.paneSwitching.showSwitcherOverlay,
|
|
1665
|
+
onChange: (checked) => props.setInteractionCapabilities(
|
|
1666
|
+
(previous) => ({
|
|
1667
|
+
...previous,
|
|
1668
|
+
paneSwitching: { ...previous.paneSwitching, showSwitcherOverlay: checked }
|
|
1669
|
+
})
|
|
1670
|
+
)
|
|
1671
|
+
}
|
|
1672
|
+
),
|
|
1673
|
+
"cmd-tab switcher overlay"
|
|
1674
|
+
]
|
|
1675
|
+
}
|
|
1676
|
+
),
|
|
1677
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded border border-white/10 bg-slate-950/60 p-1.5 font-mono text-[9px] uppercase tracking-[0.12em] text-slate-500", children: [
|
|
1678
|
+
/* @__PURE__ */ jsx("div", { className: "mb-0.5 text-slate-400", children: "keymap" }),
|
|
1679
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-0.5 normal-case tracking-normal", children: [
|
|
1680
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1681
|
+
"maximize: ",
|
|
1682
|
+
formatKeyChordLabel(props.interactionCapabilities.keymap.toggleMaximize),
|
|
1683
|
+
" | restore: ",
|
|
1684
|
+
formatKeyChordLabel(props.interactionCapabilities.keymap.restore)
|
|
1685
|
+
] }),
|
|
1686
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1687
|
+
"prev: ",
|
|
1688
|
+
formatKeyChordLabel(props.interactionCapabilities.keymap.previousPane),
|
|
1689
|
+
" | next: ",
|
|
1690
|
+
formatKeyChordLabel(props.interactionCapabilities.keymap.nextPane),
|
|
1691
|
+
" | jump: ",
|
|
1692
|
+
formatKeyModifiersLabel(props.interactionCapabilities.keymap.jumpToPane),
|
|
1693
|
+
"+1..9"
|
|
1694
|
+
] })
|
|
1695
|
+
] })
|
|
1696
|
+
] })
|
|
1697
|
+
] })
|
|
1698
|
+
] }),
|
|
1699
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1700
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "source and target routing" }),
|
|
1701
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1702
|
+
/* @__PURE__ */ jsx("label", { className: "block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: "drag source pane" }),
|
|
1703
|
+
/* @__PURE__ */ jsx(
|
|
1704
|
+
"select",
|
|
1705
|
+
{
|
|
1706
|
+
value: props.selectedSourceLeafId,
|
|
1707
|
+
onChange: (event) => {
|
|
1708
|
+
props.setSelectedSourceLeafId(event.target.value);
|
|
1709
|
+
props.setFocusedLeafId(event.target.value);
|
|
1710
|
+
},
|
|
1711
|
+
className: "w-full min-w-0 max-w-full rounded border border-white/10 bg-slate-950 px-2 py-1 font-mono text-[11px] text-slate-200",
|
|
1712
|
+
children: props.leafIds.map((leafId) => /* @__PURE__ */ jsx("option", { value: leafId, children: leafId }, `source-${leafId}`))
|
|
1713
|
+
}
|
|
1714
|
+
),
|
|
1715
|
+
/* @__PURE__ */ jsx("label", { className: "block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: "drop target pane" }),
|
|
1716
|
+
/* @__PURE__ */ jsx(
|
|
1717
|
+
"select",
|
|
1718
|
+
{
|
|
1719
|
+
value: props.selectedTargetLeafId,
|
|
1720
|
+
onChange: (event) => {
|
|
1721
|
+
props.setSelectedTargetLeafId(event.target.value);
|
|
1722
|
+
props.setFocusedLeafId(event.target.value);
|
|
1723
|
+
},
|
|
1724
|
+
className: "w-full min-w-0 max-w-full rounded border border-white/10 bg-slate-950 px-2 py-1 font-mono text-[11px] text-slate-200",
|
|
1725
|
+
children: props.leafIds.map((leafId) => /* @__PURE__ */ jsx("option", { value: leafId, children: leafId }, `target-${leafId}`))
|
|
1726
|
+
}
|
|
1727
|
+
),
|
|
1728
|
+
/* @__PURE__ */ jsx("label", { className: "block font-mono text-[10px] uppercase tracking-[0.14em] text-slate-400", children: "destination split container" }),
|
|
1729
|
+
/* @__PURE__ */ jsx(
|
|
1730
|
+
"select",
|
|
1731
|
+
{
|
|
1732
|
+
value: props.selectedSplitId,
|
|
1733
|
+
onChange: (event) => props.setSelectedSplitId(event.target.value),
|
|
1734
|
+
className: "w-full min-w-0 max-w-full rounded border border-white/10 bg-slate-950 px-2 py-1 font-mono text-[11px] text-slate-200",
|
|
1735
|
+
children: props.splitNodes.map((splitNode) => /* @__PURE__ */ jsxs("option", { value: splitNode.id, children: [
|
|
1736
|
+
splitNode.id,
|
|
1737
|
+
" (",
|
|
1738
|
+
splitNode.axis,
|
|
1739
|
+
")"
|
|
1740
|
+
] }, `split-${splitNode.id}`))
|
|
1741
|
+
}
|
|
1742
|
+
),
|
|
1743
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-300", children: [
|
|
1744
|
+
/* @__PURE__ */ jsx(
|
|
1745
|
+
StyledCheckbox,
|
|
1746
|
+
{
|
|
1747
|
+
checked: props.preserveParentSplitAxis,
|
|
1748
|
+
onChange: props.setPreserveParentSplitAxis
|
|
1749
|
+
}
|
|
1750
|
+
),
|
|
1751
|
+
"preserve parent split axis"
|
|
1752
|
+
] })
|
|
1753
|
+
] })
|
|
1754
|
+
] }),
|
|
1755
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1756
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "pane move and insert actions" }),
|
|
1757
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1758
|
+
/* @__PURE__ */ jsx(
|
|
1759
|
+
"button",
|
|
1760
|
+
{
|
|
1761
|
+
type: "button",
|
|
1762
|
+
onClick: () => props.setLayout((previous) => insertLeafAdjacent(
|
|
1763
|
+
previous,
|
|
1764
|
+
props.selectedSourceLeafId,
|
|
1765
|
+
props.selectedTargetLeafId,
|
|
1766
|
+
"left",
|
|
1767
|
+
{ preserveParentSplitAxis: props.preserveParentSplitAxis, splitRatio: 0.5 }
|
|
1768
|
+
)),
|
|
1769
|
+
className: "rounded border border-cyan-300/35 bg-cyan-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-cyan-100",
|
|
1770
|
+
children: "insert left"
|
|
1771
|
+
}
|
|
1772
|
+
),
|
|
1773
|
+
/* @__PURE__ */ jsx(
|
|
1774
|
+
"button",
|
|
1775
|
+
{
|
|
1776
|
+
type: "button",
|
|
1777
|
+
onClick: () => props.setLayout((previous) => insertLeafAdjacent(
|
|
1778
|
+
previous,
|
|
1779
|
+
props.selectedSourceLeafId,
|
|
1780
|
+
props.selectedTargetLeafId,
|
|
1781
|
+
"right",
|
|
1782
|
+
{ preserveParentSplitAxis: props.preserveParentSplitAxis, splitRatio: 0.5 }
|
|
1783
|
+
)),
|
|
1784
|
+
className: "rounded border border-cyan-300/35 bg-cyan-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-cyan-100",
|
|
1785
|
+
children: "insert right"
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ jsx(
|
|
1789
|
+
"button",
|
|
1790
|
+
{
|
|
1791
|
+
type: "button",
|
|
1792
|
+
onClick: () => props.setLayout((previous) => insertLeafAdjacent(
|
|
1793
|
+
previous,
|
|
1794
|
+
props.selectedSourceLeafId,
|
|
1795
|
+
props.selectedTargetLeafId,
|
|
1796
|
+
"top",
|
|
1797
|
+
{ preserveParentSplitAxis: props.preserveParentSplitAxis, splitRatio: 0.5 }
|
|
1798
|
+
)),
|
|
1799
|
+
className: "rounded border border-cyan-300/35 bg-cyan-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-cyan-100",
|
|
1800
|
+
children: "insert top"
|
|
1801
|
+
}
|
|
1802
|
+
),
|
|
1803
|
+
/* @__PURE__ */ jsx(
|
|
1804
|
+
"button",
|
|
1805
|
+
{
|
|
1806
|
+
type: "button",
|
|
1807
|
+
onClick: () => props.setLayout((previous) => insertLeafAdjacent(
|
|
1808
|
+
previous,
|
|
1809
|
+
props.selectedSourceLeafId,
|
|
1810
|
+
props.selectedTargetLeafId,
|
|
1811
|
+
"bottom",
|
|
1812
|
+
{ preserveParentSplitAxis: props.preserveParentSplitAxis, splitRatio: 0.5 }
|
|
1813
|
+
)),
|
|
1814
|
+
className: "rounded border border-cyan-300/35 bg-cyan-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-cyan-100",
|
|
1815
|
+
children: "insert bottom"
|
|
1816
|
+
}
|
|
1817
|
+
),
|
|
1818
|
+
/* @__PURE__ */ jsx(
|
|
1819
|
+
"button",
|
|
1820
|
+
{
|
|
1821
|
+
type: "button",
|
|
1822
|
+
onClick: () => props.setLayout((previous) => moveLeafToRoot(previous, props.selectedSourceLeafId, "first", { splitRatio: 0.5 })),
|
|
1823
|
+
className: "rounded border border-violet-300/35 bg-violet-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-violet-100",
|
|
1824
|
+
children: "move root first"
|
|
1825
|
+
}
|
|
1826
|
+
),
|
|
1827
|
+
/* @__PURE__ */ jsx(
|
|
1828
|
+
"button",
|
|
1829
|
+
{
|
|
1830
|
+
type: "button",
|
|
1831
|
+
onClick: () => props.setLayout((previous) => moveLeafToRoot(previous, props.selectedSourceLeafId, "second", { splitRatio: 0.5 })),
|
|
1832
|
+
className: "rounded border border-violet-300/35 bg-violet-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-violet-100",
|
|
1833
|
+
children: "move root second"
|
|
1834
|
+
}
|
|
1835
|
+
),
|
|
1836
|
+
/* @__PURE__ */ jsx(
|
|
1837
|
+
"button",
|
|
1838
|
+
{
|
|
1839
|
+
type: "button",
|
|
1840
|
+
onClick: () => props.setLayout((previous) => moveLeafToSplitContainer(
|
|
1841
|
+
previous,
|
|
1842
|
+
props.selectedSourceLeafId,
|
|
1843
|
+
props.selectedSplitId,
|
|
1844
|
+
"first",
|
|
1845
|
+
{ preserveParentSplitAxis: true, splitRatio: 0.5 }
|
|
1846
|
+
)),
|
|
1847
|
+
className: "rounded border border-sky-300/35 bg-sky-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-sky-100",
|
|
1848
|
+
children: "move to split first"
|
|
1849
|
+
}
|
|
1850
|
+
),
|
|
1851
|
+
/* @__PURE__ */ jsx(
|
|
1852
|
+
"button",
|
|
1853
|
+
{
|
|
1854
|
+
type: "button",
|
|
1855
|
+
onClick: () => props.setLayout((previous) => moveLeafToSplitContainer(
|
|
1856
|
+
previous,
|
|
1857
|
+
props.selectedSourceLeafId,
|
|
1858
|
+
props.selectedSplitId,
|
|
1859
|
+
"second",
|
|
1860
|
+
{ preserveParentSplitAxis: true, splitRatio: 0.5 }
|
|
1861
|
+
)),
|
|
1862
|
+
className: "rounded border border-sky-300/35 bg-sky-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-sky-100",
|
|
1863
|
+
children: "move to split second"
|
|
1864
|
+
}
|
|
1865
|
+
),
|
|
1866
|
+
/* @__PURE__ */ jsx(
|
|
1867
|
+
"button",
|
|
1868
|
+
{
|
|
1869
|
+
type: "button",
|
|
1870
|
+
onClick: () => props.setLayout((previous) => toggleSplitAxis(previous, props.selectedSplitId)),
|
|
1871
|
+
className: "col-span-2 rounded border border-pink-300/35 bg-pink-500/10 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-pink-100",
|
|
1872
|
+
children: "toggle split axis"
|
|
1873
|
+
}
|
|
1874
|
+
)
|
|
1875
|
+
] })
|
|
1876
|
+
] }),
|
|
1877
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1878
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "focus controls" }),
|
|
1879
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-4 gap-2", children: [
|
|
1880
|
+
/* @__PURE__ */ jsx(
|
|
1881
|
+
"button",
|
|
1882
|
+
{
|
|
1883
|
+
type: "button",
|
|
1884
|
+
onClick: () => props.runDirectionalFocus("left"),
|
|
1885
|
+
className: "rounded border border-white/15 bg-black/40 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-200",
|
|
1886
|
+
children: "left"
|
|
1887
|
+
}
|
|
1888
|
+
),
|
|
1889
|
+
/* @__PURE__ */ jsx(
|
|
1890
|
+
"button",
|
|
1891
|
+
{
|
|
1892
|
+
type: "button",
|
|
1893
|
+
onClick: () => props.runDirectionalFocus("right"),
|
|
1894
|
+
className: "rounded border border-white/15 bg-black/40 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-200",
|
|
1895
|
+
children: "right"
|
|
1896
|
+
}
|
|
1897
|
+
),
|
|
1898
|
+
/* @__PURE__ */ jsx(
|
|
1899
|
+
"button",
|
|
1900
|
+
{
|
|
1901
|
+
type: "button",
|
|
1902
|
+
onClick: () => props.runDirectionalFocus("up"),
|
|
1903
|
+
className: "rounded border border-white/15 bg-black/40 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-200",
|
|
1904
|
+
children: "up"
|
|
1905
|
+
}
|
|
1906
|
+
),
|
|
1907
|
+
/* @__PURE__ */ jsx(
|
|
1908
|
+
"button",
|
|
1909
|
+
{
|
|
1910
|
+
type: "button",
|
|
1911
|
+
onClick: () => props.runDirectionalFocus("down"),
|
|
1912
|
+
className: "rounded border border-white/15 bg-black/40 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-200",
|
|
1913
|
+
children: "down"
|
|
1914
|
+
}
|
|
1915
|
+
)
|
|
1916
|
+
] }),
|
|
1917
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 font-mono text-[10px] uppercase tracking-[0.12em] text-slate-500", children: "arrows also drive focus navigation" })
|
|
1918
|
+
] }),
|
|
1919
|
+
props.previewOverlaysSticky ? null : previewOverlaysGroup,
|
|
1920
|
+
props.subjectColorsSticky ? null : subjectColorsGroup,
|
|
1921
|
+
props.dropIntentDebugSticky ? null : dropIntentDebugGroup,
|
|
1922
|
+
props.hitZoneOverlaysSticky ? null : hitZoneOverlaysGroup,
|
|
1923
|
+
props.hitZoneGeometrySticky ? null : hitZoneGeometryGroup,
|
|
1924
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1925
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "basic layout tuning" }),
|
|
1926
|
+
/* @__PURE__ */ jsxs("label", { className: "block font-mono text-[10px] uppercase tracking-[0.15em] text-slate-400", children: [
|
|
1927
|
+
"gap px: ",
|
|
1928
|
+
props.config.gapPx
|
|
1929
|
+
] }),
|
|
1930
|
+
/* @__PURE__ */ jsx(
|
|
1931
|
+
RangeSlider,
|
|
1932
|
+
{
|
|
1933
|
+
min: 4,
|
|
1934
|
+
max: 24,
|
|
1935
|
+
step: 1,
|
|
1936
|
+
value: props.config.gapPx,
|
|
1937
|
+
onChange: (value) => props.setConfig((previous) => ({
|
|
1938
|
+
...previous,
|
|
1939
|
+
gapPx: value
|
|
1940
|
+
}))
|
|
1941
|
+
}
|
|
1942
|
+
),
|
|
1943
|
+
/* @__PURE__ */ jsxs("label", { className: "mt-3 block font-mono text-[10px] uppercase tracking-[0.15em] text-slate-400", children: [
|
|
1944
|
+
"min pane px: ",
|
|
1945
|
+
props.config.minPaneSizePx
|
|
1946
|
+
] }),
|
|
1947
|
+
/* @__PURE__ */ jsx(
|
|
1948
|
+
RangeSlider,
|
|
1949
|
+
{
|
|
1950
|
+
min: 80,
|
|
1951
|
+
max: 260,
|
|
1952
|
+
step: 10,
|
|
1953
|
+
value: props.config.minPaneSizePx,
|
|
1954
|
+
onChange: (value) => props.setConfig((previous) => ({
|
|
1955
|
+
...previous,
|
|
1956
|
+
minPaneSizePx: value
|
|
1957
|
+
}))
|
|
1958
|
+
}
|
|
1959
|
+
)
|
|
1960
|
+
] })
|
|
1961
|
+
] }),
|
|
1962
|
+
/* @__PURE__ */ jsxs("section", { className: "mt-3 min-w-0 max-w-full space-y-2 overflow-hidden rounded border border-white/10 bg-slate-900/70 p-2", children: [
|
|
1963
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[10px] uppercase tracking-[0.14em] text-cyan-200", children: "diagnostics" }),
|
|
1964
|
+
/* @__PURE__ */ jsxs("details", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1965
|
+
/* @__PURE__ */ jsxs("summary", { className: "cursor-pointer list-none font-mono text-[10px] uppercase tracking-[0.13em] text-slate-300", children: [
|
|
1966
|
+
"stream ledger (",
|
|
1967
|
+
props.observabilityLedgerEntries.length,
|
|
1968
|
+
"/",
|
|
1969
|
+
LIVE_LEDGER_RETENTION_LIMIT,
|
|
1970
|
+
")"
|
|
1971
|
+
] }),
|
|
1972
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 max-h-[180px] min-h-0 min-w-0 max-w-full overflow-y-auto overflow-x-hidden rounded border border-white/10 bg-slate-950/85 p-2", children: props.observabilityLedgerEntries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "font-mono text-[10px] uppercase tracking-[0.12em] text-slate-500", children: "waiting for hover or drag activity" }) : /* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-1", children: props.observabilityLedgerEntries.map((ledgerEntry) => /* @__PURE__ */ jsx(
|
|
1973
|
+
"div",
|
|
1974
|
+
{
|
|
1975
|
+
className: "w-full min-w-0 overflow-x-auto overflow-y-hidden font-mono text-[10px] leading-4 tracking-[0.08em] text-slate-200",
|
|
1976
|
+
children: /* @__PURE__ */ jsxs("div", { className: "inline-block min-w-max whitespace-nowrap", children: [
|
|
1977
|
+
/* @__PURE__ */ jsx("span", { className: "text-cyan-200", children: ledgerEntry.timestampLabel }),
|
|
1978
|
+
" ",
|
|
1979
|
+
ledgerEntry.streamLine
|
|
1980
|
+
] })
|
|
1981
|
+
},
|
|
1982
|
+
ledgerEntry.id
|
|
1983
|
+
)) }) })
|
|
1984
|
+
] }),
|
|
1985
|
+
/* @__PURE__ */ jsxs("details", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
1986
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none font-mono text-[10px] uppercase tracking-[0.13em] text-slate-300", children: "hit resolver snapshot" }),
|
|
1987
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 min-w-0 max-w-full space-y-1 overflow-x-auto overflow-y-hidden font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
1988
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1989
|
+
"hovered pane id: ",
|
|
1990
|
+
props.liveHitLog?.hoveredLeafId ?? "none"
|
|
1991
|
+
] }),
|
|
1992
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1993
|
+
"source leaf id (hovered pane): ",
|
|
1994
|
+
props.liveHitLog?.sourceLeafId ?? "none"
|
|
1995
|
+
] }),
|
|
1996
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1997
|
+
"drag source leaf id (drag origin): ",
|
|
1998
|
+
props.liveHitLog?.dragSourceLeafId ?? "none"
|
|
1999
|
+
] }),
|
|
2000
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2001
|
+
"pointer mode: ",
|
|
2002
|
+
props.liveHitLog?.isDragging ? "dragging" : "hover/move"
|
|
2003
|
+
] }),
|
|
2004
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2005
|
+
"resolver zone: ",
|
|
2006
|
+
props.liveHitLog?.resolverZone ?? "none"
|
|
2007
|
+
] }),
|
|
2008
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2009
|
+
"center ratio: ",
|
|
2010
|
+
props.liveHitLog?.centerRatio?.toFixed(2) ?? "none"
|
|
2011
|
+
] }),
|
|
2012
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2013
|
+
"edge threshold ratio: ",
|
|
2014
|
+
props.liveHitLog?.edgeThresholdRatio?.toFixed(2) ?? "none"
|
|
2015
|
+
] }),
|
|
2016
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2017
|
+
"center rect px: ",
|
|
2018
|
+
props.liveHitLog == null ? "none" : `${props.liveHitLog.centerRectWidthPx.toFixed(1)} \xD7 ${props.liveHitLog.centerRectHeightPx.toFixed(1)}`
|
|
2019
|
+
] }),
|
|
2020
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2021
|
+
"center valid: ",
|
|
2022
|
+
props.liveHitLog?.centerIsValid == null ? "none" : props.liveHitLog.centerIsValid ? "valid" : "blocked"
|
|
2023
|
+
] }),
|
|
2024
|
+
/* @__PURE__ */ jsxs(
|
|
2025
|
+
"div",
|
|
2026
|
+
{
|
|
2027
|
+
className: "overflow-y-auto overflow-x-auto whitespace-nowrap normal-case",
|
|
2028
|
+
style: {
|
|
2029
|
+
height: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2030
|
+
minHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2031
|
+
maxHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2032
|
+
lineHeight: CONTROL_PANE_STATUS_LINE_HEIGHT_PX / 16
|
|
2033
|
+
},
|
|
2034
|
+
children: [
|
|
2035
|
+
"center blocked reason: ",
|
|
2036
|
+
normalizeReasonToken(props.liveHitLog?.centerBlockedReason)
|
|
2037
|
+
]
|
|
2038
|
+
}
|
|
2039
|
+
),
|
|
2040
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2041
|
+
"center distance px: ",
|
|
2042
|
+
props.liveHitLog?.intent?.centerDistancePx?.toFixed(1) ?? "none"
|
|
2043
|
+
] }),
|
|
2044
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2045
|
+
"nearest edge distance px: ",
|
|
2046
|
+
props.liveHitLog?.intent?.nearestEdgeDistancePx?.toFixed(1) ?? "none"
|
|
2047
|
+
] }),
|
|
2048
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2049
|
+
"selected edge distance px: ",
|
|
2050
|
+
props.liveHitLog?.intent?.selectedSplitDistancePx?.toFixed(1) ?? "none"
|
|
2051
|
+
] }),
|
|
2052
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2053
|
+
"selected edge candidate: ",
|
|
2054
|
+
props.liveHitLog?.intent?.selectedSplitZone ?? "none"
|
|
2055
|
+
] }),
|
|
2056
|
+
/* @__PURE__ */ jsx("div", { children: "edge validity:" }),
|
|
2057
|
+
props.liveHitLog == null || props.liveHitLog.edgeDiagnostics.length === 0 ? /* @__PURE__ */ jsx("div", { className: "pl-2 text-slate-500", children: "none" }) : props.liveHitLog.edgeDiagnostics.map((edgeDiagnostic) => /* @__PURE__ */ jsxs("div", { className: "pl-2 text-slate-200", children: [
|
|
2058
|
+
edgeDiagnostic.zone,
|
|
2059
|
+
": ",
|
|
2060
|
+
edgeDiagnostic.isValid ? "valid" : "blocked",
|
|
2061
|
+
" | reason",
|
|
2062
|
+
" ",
|
|
2063
|
+
edgeDiagnostic.rejectionReason ?? "none"
|
|
2064
|
+
] }, `live-hit-edge-${edgeDiagnostic.zone}`))
|
|
2065
|
+
] })
|
|
2066
|
+
] }),
|
|
2067
|
+
/* @__PURE__ */ jsxs("details", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
2068
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none font-mono text-[10px] uppercase tracking-[0.13em] text-slate-300", children: "drop intent internals" }),
|
|
2069
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 min-w-0 max-w-full space-y-1 overflow-x-auto overflow-y-hidden font-mono text-[10px] uppercase tracking-[0.12em] text-slate-300", children: [
|
|
2070
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2071
|
+
"resolved action: ",
|
|
2072
|
+
props.liveDropIntent?.action ?? "none"
|
|
2073
|
+
] }),
|
|
2074
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2075
|
+
"dominant edge: ",
|
|
2076
|
+
props.liveDropIntent?.dominantEdge ?? "none"
|
|
2077
|
+
] }),
|
|
2078
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2079
|
+
"selected final edge: ",
|
|
2080
|
+
props.liveDropIntent?.finalEdge ?? "none"
|
|
2081
|
+
] }),
|
|
2082
|
+
/* @__PURE__ */ jsxs(
|
|
2083
|
+
"div",
|
|
2084
|
+
{
|
|
2085
|
+
className: "overflow-y-auto overflow-x-auto whitespace-nowrap normal-case",
|
|
2086
|
+
style: {
|
|
2087
|
+
height: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2088
|
+
minHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2089
|
+
maxHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2090
|
+
lineHeight: CONTROL_PANE_STATUS_LINE_HEIGHT_PX / 16
|
|
2091
|
+
},
|
|
2092
|
+
children: [
|
|
2093
|
+
"fallback reason: ",
|
|
2094
|
+
normalizeReasonToken(props.liveDropIntent?.fallbackReason)
|
|
2095
|
+
]
|
|
2096
|
+
}
|
|
2097
|
+
),
|
|
2098
|
+
/* @__PURE__ */ jsxs(
|
|
2099
|
+
"div",
|
|
2100
|
+
{
|
|
2101
|
+
className: "overflow-y-auto overflow-x-auto whitespace-nowrap normal-case",
|
|
2102
|
+
style: {
|
|
2103
|
+
height: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2104
|
+
minHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2105
|
+
maxHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2106
|
+
lineHeight: CONTROL_PANE_STATUS_LINE_HEIGHT_PX / 16
|
|
2107
|
+
},
|
|
2108
|
+
children: [
|
|
2109
|
+
"blocked reason: ",
|
|
2110
|
+
normalizeReasonToken(props.liveDropIntent?.blockedReason)
|
|
2111
|
+
]
|
|
2112
|
+
}
|
|
2113
|
+
),
|
|
2114
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2115
|
+
"axis path / depth: ",
|
|
2116
|
+
props.liveDropIntent == null ? "none" : `${axisPathLabel(props.liveDropIntent.axisPath)} (d=${props.liveDropIntent.axisPath.length})`
|
|
2117
|
+
] }),
|
|
2118
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2119
|
+
"edge threshold ratio: ",
|
|
2120
|
+
props.liveDropIntent?.edgeThresholdRatio?.toFixed(2) ?? "none"
|
|
2121
|
+
] }),
|
|
2122
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2123
|
+
"center rect px: ",
|
|
2124
|
+
props.liveDropIntent == null ? "none" : `${props.liveDropIntent.centerRectWidthPx?.toFixed(1) ?? "none"} \xD7 ${props.liveDropIntent.centerRectHeightPx?.toFixed(1) ?? "none"}`
|
|
2125
|
+
] }),
|
|
2126
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2127
|
+
"pane-local cursor px: ",
|
|
2128
|
+
props.liveDropIntent == null ? "none" : `${props.liveDropIntent.paneLocalX?.toFixed(1) ?? "none"}, ${props.liveDropIntent.paneLocalY?.toFixed(1) ?? "none"}`
|
|
2129
|
+
] }),
|
|
2130
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2131
|
+
"center distance px: ",
|
|
2132
|
+
props.liveDropIntent?.centerDistancePx?.toFixed(1) ?? "none"
|
|
2133
|
+
] }),
|
|
2134
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2135
|
+
"nearest edge distance px: ",
|
|
2136
|
+
props.liveDropIntent?.nearestEdgeDistancePx?.toFixed(1) ?? "none"
|
|
2137
|
+
] }),
|
|
2138
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2139
|
+
"selected split candidate: ",
|
|
2140
|
+
props.liveDropIntent?.selectedSplitZone ?? "none"
|
|
2141
|
+
] }),
|
|
2142
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2143
|
+
"selected split distance px: ",
|
|
2144
|
+
props.liveDropIntent?.selectedSplitDistancePx?.toFixed(1) ?? "none"
|
|
2145
|
+
] }),
|
|
2146
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2147
|
+
"center ratio: ",
|
|
2148
|
+
props.liveDropIntent?.tuning.centerRatio?.toFixed(2) ?? "none"
|
|
2149
|
+
] }),
|
|
2150
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2151
|
+
"hysteresis px: ",
|
|
2152
|
+
props.liveDropIntent?.tuning.hysteresisPx?.toFixed(1) ?? "none"
|
|
2153
|
+
] }),
|
|
2154
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2155
|
+
"device pixel ratio: ",
|
|
2156
|
+
props.liveDropIntent?.tuning.devicePixelRatio?.toFixed(2) ?? "none"
|
|
2157
|
+
] }),
|
|
2158
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2159
|
+
"target split id: ",
|
|
2160
|
+
props.liveDropIntent?.targetSplitId ?? "none"
|
|
2161
|
+
] }),
|
|
2162
|
+
/* @__PURE__ */ jsxs(
|
|
2163
|
+
"div",
|
|
2164
|
+
{
|
|
2165
|
+
className: "overflow-y-auto overflow-x-auto whitespace-nowrap normal-case",
|
|
2166
|
+
style: {
|
|
2167
|
+
height: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2168
|
+
minHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2169
|
+
maxHeight: CONTROL_PANE_REASON_RESERVED_HEIGHT_PX,
|
|
2170
|
+
lineHeight: CONTROL_PANE_STATUS_LINE_HEIGHT_PX / 16
|
|
2171
|
+
},
|
|
2172
|
+
children: [
|
|
2173
|
+
"rejected split reasons:",
|
|
2174
|
+
" ",
|
|
2175
|
+
props.liveDropIntent == null ? "none" : normalizeReasonList(props.liveDropIntent.rejectedSplitReasons)
|
|
2176
|
+
]
|
|
2177
|
+
}
|
|
2178
|
+
)
|
|
2179
|
+
] })
|
|
2180
|
+
] }),
|
|
2181
|
+
/* @__PURE__ */ jsxs("details", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-950/60 p-2", children: [
|
|
2182
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none font-mono text-[10px] uppercase tracking-[0.13em] text-slate-300", children: "layout state snapshot" }),
|
|
2183
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 min-w-0 max-w-full space-y-2 overflow-hidden", children: [
|
|
2184
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-2 font-mono text-[10px] uppercase tracking-[0.13em]", children: [
|
|
2185
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded border border-white/10 bg-slate-900/80 p-2", children: [
|
|
2186
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", children: "split nodes" }),
|
|
2187
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 text-cyan-200", children: props.splitCount })
|
|
2188
|
+
] }),
|
|
2189
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded border border-white/10 bg-slate-900/80 p-2", children: [
|
|
2190
|
+
/* @__PURE__ */ jsx("div", { className: "text-slate-500", children: "leaf nodes" }),
|
|
2191
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 text-cyan-200", children: props.leafIds.length })
|
|
2192
|
+
] })
|
|
2193
|
+
] }),
|
|
2194
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-x-auto overflow-y-hidden rounded border border-white/10 bg-slate-900/80 p-2", children: [
|
|
2195
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "tile order by leaf traversal" }),
|
|
2196
|
+
/* @__PURE__ */ jsx("div", { className: "mt-1 font-mono text-[11px] text-slate-200", children: props.tileOrder.join(" -> ") })
|
|
2197
|
+
] }),
|
|
2198
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-hidden rounded border border-white/10 bg-slate-900/80 p-2", children: [
|
|
2199
|
+
/* @__PURE__ */ jsx("div", { className: "font-mono text-[10px] uppercase tracking-[0.13em] text-slate-500", children: "active focus leaf" }),
|
|
2200
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2 font-mono text-[11px] text-violet-100", children: [
|
|
2201
|
+
/* @__PURE__ */ jsx(
|
|
2202
|
+
"span",
|
|
2203
|
+
{
|
|
2204
|
+
className: `${CONTROL_PANE_STATUS_BADGE_WIDTH_CLASS} inline-flex items-center justify-center rounded-full border border-violet-200/60 bg-violet-500/20 px-2 py-0.5 text-[9px] font-semibold uppercase tracking-[0.14em] text-violet-100`,
|
|
2205
|
+
children: "focused"
|
|
2206
|
+
}
|
|
2207
|
+
),
|
|
2208
|
+
props.focusedLeafId ?? "none"
|
|
2209
|
+
] })
|
|
2210
|
+
] }),
|
|
2211
|
+
/* @__PURE__ */ jsxs("div", { className: "max-h-[45vh] min-h-[180px] min-w-0 max-w-full overflow-y-auto overflow-x-auto rounded border border-white/10 bg-slate-950/80 p-2", children: [
|
|
2212
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 font-mono text-[10px] uppercase tracking-[0.14em] text-slate-500", children: "live layout json" }),
|
|
2213
|
+
/* @__PURE__ */ jsx("pre", { className: "w-max min-w-full font-mono text-[10px] leading-4 text-slate-300", children: JSON.stringify(props.layout, null, 2) })
|
|
2214
|
+
] }),
|
|
2215
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-x-auto overflow-y-hidden rounded border border-white/10 bg-slate-950/70 px-2 py-1 font-mono text-[10px] uppercase tracking-[0.13em] text-slate-300", children: [
|
|
2216
|
+
"projected overlays rendered: ",
|
|
2217
|
+
props.projectedOverlayRenderCount
|
|
2218
|
+
] }),
|
|
2219
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-x-auto overflow-y-hidden rounded border border-white/10 bg-slate-950/70 px-2 py-1 font-mono text-[9px] tracking-[0.08em] text-slate-300", children: [
|
|
2220
|
+
/* @__PURE__ */ jsx("div", { className: "uppercase text-slate-500", children: "overlay border legend" }),
|
|
2221
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
2222
|
+
"drag source:",
|
|
2223
|
+
" ",
|
|
2224
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block rounded border px-1", style: { borderColor: props.observabilityColors.dragSourceBorderColorHex }, children: "custom" }),
|
|
2225
|
+
" ",
|
|
2226
|
+
"| drop target:",
|
|
2227
|
+
" ",
|
|
2228
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block rounded border px-1", style: { borderColor: props.observabilityColors.dragTargetBorderColorHex }, children: "custom" })
|
|
2229
|
+
] }),
|
|
2230
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2231
|
+
"projected S' (source landing):",
|
|
2232
|
+
" ",
|
|
2233
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block rounded border px-1", style: { borderColor: props.observabilityColors.projectedSourceBorderColorHex }, children: "custom" }),
|
|
2234
|
+
" ",
|
|
2235
|
+
"| projected T' (target landing, swap only):",
|
|
2236
|
+
" ",
|
|
2237
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block rounded border px-1", style: { borderColor: props.observabilityColors.projectedTargetBorderColorHex }, children: "custom" })
|
|
2238
|
+
] }),
|
|
2239
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2240
|
+
"projected Su' (successor promotion, insert/move):",
|
|
2241
|
+
" ",
|
|
2242
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block rounded border px-1", style: { borderColor: props.observabilityColors.projectedSuccessorBorderColorHex }, children: "custom" })
|
|
2243
|
+
] })
|
|
2244
|
+
] }),
|
|
2245
|
+
props.showPaneHitZones ? /* @__PURE__ */ jsxs("div", { className: "min-w-0 max-w-full overflow-x-auto overflow-y-hidden rounded border border-white/10 bg-slate-950/70 px-2 py-1 font-mono text-[9px] tracking-[0.08em] text-slate-300", children: [
|
|
2246
|
+
/* @__PURE__ */ jsx("div", { className: "uppercase text-slate-500", children: "pane hit-zone legend" }),
|
|
2247
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
2248
|
+
"edge split zones:",
|
|
2249
|
+
" ",
|
|
2250
|
+
/* @__PURE__ */ jsx("span", { className: "rounded border px-1 text-sky-100", style: { borderColor: props.observabilityColors.hitZoneLeftColorHex, backgroundColor: `${props.observabilityColors.hitZoneLeftColorHex}33` }, children: "left" }),
|
|
2251
|
+
" ",
|
|
2252
|
+
/* @__PURE__ */ jsx("span", { className: "rounded border px-1 text-violet-100", style: { borderColor: props.observabilityColors.hitZoneRightColorHex, backgroundColor: `${props.observabilityColors.hitZoneRightColorHex}33` }, children: "right" }),
|
|
2253
|
+
" ",
|
|
2254
|
+
/* @__PURE__ */ jsx("span", { className: "rounded border px-1 text-amber-100", style: { borderColor: props.observabilityColors.hitZoneTopColorHex, backgroundColor: `${props.observabilityColors.hitZoneTopColorHex}33` }, children: "top" }),
|
|
2255
|
+
" ",
|
|
2256
|
+
/* @__PURE__ */ jsx("span", { className: "rounded border px-1 text-teal-100", style: { borderColor: props.observabilityColors.hitZoneBottomColorHex, backgroundColor: `${props.observabilityColors.hitZoneBottomColorHex}33` }, children: "bottom" })
|
|
2257
|
+
] }),
|
|
2258
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2259
|
+
"center swap zone: ",
|
|
2260
|
+
/* @__PURE__ */ jsx("span", { className: "rounded border px-1 text-emerald-100", style: { borderColor: props.observabilityColors.hitZoneCenterColorHex, backgroundColor: `${props.observabilityColors.hitZoneCenterColorHex}33` }, children: "center rect" })
|
|
2261
|
+
] }),
|
|
2262
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2263
|
+
"blocked edge/center: ",
|
|
2264
|
+
/* @__PURE__ */ jsx("span", { className: "rounded border px-1 text-rose-100", style: { borderColor: props.observabilityColors.hitZoneBlockedColorHex, backgroundColor: `${props.observabilityColors.hitZoneBlockedColorHex}33` }, children: "blocked" })
|
|
2265
|
+
] }),
|
|
2266
|
+
/* @__PURE__ */ jsxs("div", { className: "text-slate-400", children: [
|
|
2267
|
+
"validity source leaf: ",
|
|
2268
|
+
props.selectedSourceLeafId
|
|
2269
|
+
] })
|
|
2270
|
+
] }) : null
|
|
2271
|
+
] })
|
|
2272
|
+
] })
|
|
2273
|
+
] })
|
|
2274
|
+
] })
|
|
2275
|
+
] })
|
|
2276
|
+
]
|
|
2277
|
+
}
|
|
2278
|
+
);
|
|
2279
|
+
}
|
|
2280
|
+
export {
|
|
2281
|
+
ANIMATION_CONTROL_DEFAULTS,
|
|
2282
|
+
TILING_OBSERVABILITY_COLOR_DEFAULTS,
|
|
2283
|
+
TILING_OBSERVABILITY_COLOR_ENABLE_DEFAULTS,
|
|
2284
|
+
TilingObservabilityPanel,
|
|
2285
|
+
TilingRendererWithObservability as TilingRenderer
|
|
2286
|
+
};
|