From 510fd8cbe53abb39cba2c7cbaaefcf2783dc0066 Mon Sep 17 00:00:00 2001
From: "netop://ウィビ" <paul@webb.page>
Date: Fri, 24 Apr 2026 16:37:33 -0700
Subject: Implement v0.6-1.0: shortcuts, format, export/import, splitter,
 timing, APQ

- v0.6: matchShortcut + format(); Cmd+Shift+Enter/W/F + Cmd+Alt+arrows
- v0.7: SessionStore.exportAll/importTabs with version-1 validator
- v0.8: Splitter component + four resize handles persisted under layout.*
- v0.10: createApqFetcher (HTTP-only) wrapping shared http-body helpers
- Drop .svelte re-exports from index.ts for multi-entry JSR/npm publishing
---
 source/library/components/Splitter.svelte | 123 ++++++++++++++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 source/library/components/Splitter.svelte

(limited to 'source/library/components/Splitter.svelte')

diff --git a/source/library/components/Splitter.svelte b/source/library/components/Splitter.svelte
new file mode 100644
index 0000000..f4138f2
--- /dev/null
+++ b/source/library/components/Splitter.svelte
@@ -0,0 +1,123 @@
+<script lang="ts">
+  type Props = {
+    onDrag: (dx: number, dy: number) => void;
+    onDragEnd?: () => void;
+    onDragStart?: () => void;
+    onKeyAdjust?: (delta: number) => void;
+    orientation: "horizontal" | "vertical";
+  };
+
+  let { onDrag, onDragEnd, onDragStart, onKeyAdjust, orientation }: Props = $props();
+
+  let dragging = $state(false);
+  let startX = $state(0);
+  let startY = $state(0);
+
+  function onPointerDown(event: PointerEvent) {
+    event.preventDefault();
+    dragging = true;
+    startX = event.clientX;
+    startY = event.clientY;
+    event.currentTarget instanceof Element &&
+      event.currentTarget.setPointerCapture(event.pointerId);
+    onDragStart?.();
+  }
+
+  function onPointerMove(event: PointerEvent) {
+    if (!dragging)
+      return;
+
+    onDrag(event.clientX - startX, event.clientY - startY);
+  }
+
+  function onPointerUp(event: PointerEvent) {
+    if (!dragging)
+      return;
+
+    dragging = false;
+    event.currentTarget instanceof Element &&
+      event.currentTarget.releasePointerCapture(event.pointerId);
+    onDragEnd?.();
+  }
+
+  function onKeydown(event: KeyboardEvent) {
+    if (!onKeyAdjust)
+      return;
+
+    if (orientation === "horizontal") {
+      if (event.key === "ArrowLeft") {
+        event.preventDefault();
+        onKeyAdjust(-16);
+      } else if (event.key === "ArrowRight") {
+        event.preventDefault();
+        onKeyAdjust(16);
+      }
+    } else {
+      if (event.key === "ArrowUp") {
+        event.preventDefault();
+        onKeyAdjust(-16);
+      } else if (event.key === "ArrowDown") {
+        event.preventDefault();
+        onKeyAdjust(16);
+      }
+    }
+  }
+</script>
+
+<style lang="scss">
+  .splitter {
+    align-items: center;
+    background: transparent;
+    display: flex;
+    justify-content: center;
+    position: relative;
+    touch-action: none;
+    user-select: none;
+
+    &:hover::after,
+    &.dragging::after {
+      background: var(--graphiql-accent, #0e639c);
+    }
+
+    &::after {
+      background: var(--graphiql-border, #333);
+      content: "";
+      position: absolute;
+      transition: background 120ms ease;
+    }
+
+    &.horizontal {
+      cursor: col-resize;
+      height: 100%;
+      width: 6px;
+
+      &::after {
+        height: 100%;
+        width: 1px;
+      }
+    }
+
+    &.vertical {
+      cursor: row-resize;
+      height: 6px;
+      width: 100%;
+
+      &::after {
+        height: 1px;
+        width: 100%;
+      }
+    }
+  }
+</style>
+
+<div
+  aria-orientation={orientation}
+  class="splitter {orientation}"
+  class:dragging
+  onkeydown={onKeydown}
+  onpointerdown={onPointerDown}
+  onpointermove={onPointerMove}
+  onpointerup={onPointerUp}
+  role="separator"
+  tabindex="0"
+></div>
-- 
cgit v1.2.3

