+
+ {@render panelHead()}
+ {@render addRow(0)}
+ {#each steps as step, i (step.key)}
+ {@render indicator(i)}
+ {@render stepCard(step, i)}
+
+ {@render inputTile(i)}
+ {@render outputTile(i)}
+
+ {@render addRow(i + 1)}
+ {/each}
+ {@render indicator(steps.length)}
+
+
(textSource = v)}
- onrendertext={run}
- onupload={handleFile}
- onuploadmany={handleFiles}
- ondownload={download}
- oncopy={copyText}
- ondownloadtxt={downloadText}
- onformat={(v) => (format = v)}
- onquality={setFormatQuality}
- onlimit={(v) => (limitKb = v)}
- />
{/if}
+ {#if errorText}
+ {errorText}
+ {/if}
+ {#each allWarnings as w (warningText(w))}
+ {warningText(w)}
+ {/each}
{#if stepDnd.activeData && stepDnd.pointer}
:global(.step-card) {
+ grid-column: 1;
+ }
+ .aligned .head-spacer {
+ grid-column: 1;
+ }
+ .aligned .panel-head {
+ grid-column: 2 / -1;
+ padding: var(--space-l) var(--space-xl);
+ border: var(--size-border) solid var(--color-border);
+ border-bottom: none;
+ border-radius: var(--radius-m) var(--radius-m) 0 0;
+ background: var(--color-panel);
+ margin-bottom: 0;
+ }
+ .aligned .pair {
+ grid-column: 2 / -1;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: var(--space-l);
padding: var(--space-xl);
+ border-right: var(--size-border) solid var(--color-border);
+ border-left: var(--size-border) solid var(--color-border);
+ background: var(--color-panel);
+ }
+ /* The workspace has align-items: start; panel pieces must stretch to the
+ row height or the side borders break when a card is taller than its
+ pair. */
+ .aligned .panel-head,
+ .aligned .pair,
+ .aligned .row-bridge,
+ .aligned .panel-foot {
+ align-self: stretch;
+ }
+ .aligned .add-row,
+ .aligned .indicator-row {
+ grid-column: 1 / -1;
+ display: grid;
+ grid-template-columns: subgrid;
+ }
+ .aligned .row-bridge {
+ grid-column: 2 / -1;
+ border-right: var(--size-border) solid var(--color-border);
+ border-left: var(--size-border) solid var(--color-border);
+ background: var(--color-panel);
+ }
+ .aligned .panel-foot {
+ grid-column: 2 / -1;
+ min-height: var(--space-xl);
+ border: var(--size-border) solid var(--color-border);
+ border-top: none;
+ border-radius: 0 0 var(--radius-m) var(--radius-m);
+ background: var(--color-panel);
+ }
+ /* Compact drag: while a step is dragged, cards collapse to headers so the
+ reorder happens in a tight list; aligned additionally drops the preview
+ panel (its rows are too tall to drag across). */
+ .dnd-active :global(.step-body) {
+ display: none;
+ }
+ .aligned.dnd-active .panel-head,
+ .aligned.dnd-active .pair,
+ .aligned.dnd-active .row-bridge,
+ .aligned.dnd-active .panel-foot,
+ .aligned.dnd-active .head-spacer {
+ display: none;
+ }
+ .error {
+ margin: var(--space-l) 0 0;
+ color: var(--color-danger);
+ font: var(--font-size-s) var(--font-mono);
+ }
+ .empty {
+ font: var(--font-size-s) var(--font-mono);
}
.no-schema {
font: var(--font-size-s) var(--font-mono);
}
.warn {
- margin: 0;
+ margin: var(--space-s) 0 0;
color: var(--color-warning);
font: var(--font-size-s) var(--font-mono);
}
@media (--bp-tablet) {
- .workspace {
+ .workspace,
+ .workspace.aligned {
grid-template-columns: 1fr;
}
+ .pair-grid,
+ .aligned .pair {
+ grid-template-columns: 1fr;
+ }
+ .aligned .head-spacer,
+ .aligned .row-bridge {
+ display: none;
+ }
+ .aligned .panel-head,
+ .aligned .pair,
+ .aligned .panel-foot {
+ grid-column: 1 / -1;
+ }
}
diff --git a/web/src/lib/components/schema/step-dnd.svelte.ts b/web/src/lib/components/schema/step-dnd.svelte.ts
index 8a4ac9f..746275e 100644
--- a/web/src/lib/components/schema/step-dnd.svelte.ts
+++ b/web/src/lib/components/schema/step-dnd.svelte.ts
@@ -20,14 +20,17 @@ interface DndEvent {
}
// Count the slot the pointer is over: how many non-source cards have their
-// vertical midpoint above the pointer.
+// vertical midpoint above the pointer. Also reports the height of the first
+// visible card: during drag all cards collapse to headers, so the indicator
+// takes that uniform height rather than the hidden source's stale height.
function indexAtPosition(
source: SortableDraggable,
pointerY: number,
-): number {
+): { index: number; cardHeight: number } {
const manager = source.sortable.manager;
let index = 0;
- if (!manager) return index;
+ let cardHeight = 0;
+ if (!manager) return { index, cardHeight };
for (const droppable of manager.registry.droppables) {
if (!isSortable(droppable)) continue;
if (droppable.sortable.id === source.id) continue;
@@ -35,9 +38,10 @@ function indexAtPosition(
if (!el) continue;
const rect = el.getBoundingClientRect();
if (rect.height === 0) continue;
+ if (!cardHeight) cardHeight = rect.height;
if (pointerY > rect.top + rect.height / 2) index++;
}
- return index;
+ return { index, cardHeight };
}
// Step-reorder state for SchemaToolView, single list. The dragged card is
@@ -65,10 +69,12 @@ export function createStepDnd({ onMove }: StepDndOptions) {
const position = event.operation?.position?.current;
if (!position || !sourceX) return null;
if (position.x < sourceX.left || position.x > sourceX.right) return null;
- return indexAtPosition(
+ const { index, cardHeight } = indexAtPosition(
source as SortableDraggable,
position.y,
);
+ if (cardHeight) dragHeight = cardHeight;
+ return index;
}
function onDragStart(event: DndEvent) {