File size: 4,232 Bytes
39371ea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
const STORAGE_KEY = 'pi-column-ratio';
const DEFAULT_RATIO = 1 / 1.83;

export function createColumnResizer(layout, handle) {
  const desktop = matchMedia('(min-width: 851px)');
  const style = getComputedStyle(layout);
  const minimumChat = parseFloat(style.getPropertyValue('--conversation-min'));
  const minimumWorkspace = parseFloat(style.getPropertyValue('--workspace-min'));
  let ratio = DEFAULT_RATIO, drag, frame = 0;
  try {
    const saved = Number(localStorage.getItem(STORAGE_KEY));
    if (Number.isFinite(saved) && saved > 0 && saved < 1) ratio = saved;
  } catch { /* The divider also works when browser storage is unavailable. */ }

  function apply() {
    if (!desktop.matches) return;
    const width = layout.getBoundingClientRect().width;
    if (!width) return;
    const chat = Math.max(minimumChat, Math.min(width - minimumWorkspace, width * ratio));
    layout.style.setProperty('--conversation-width', `${chat}px`);
    const percent = Math.round(chat / width * 100);
    handle.setAttribute('aria-valuemin', String(Math.round(minimumChat / width * 100)));
    handle.setAttribute('aria-valuemax', String(Math.round((width - minimumWorkspace) / width * 100)));
    handle.setAttribute('aria-valuenow', String(percent));
    handle.setAttribute('aria-valuetext', `Chat ${percent}%, workspace ${100 - percent}%`);
  }
  function save() { try { localStorage.setItem(STORAGE_KEY, String(ratio)); } catch {} }
  function setWidth(pixels) {
    const width = layout.getBoundingClientRect().width;
    ratio = Math.max(minimumChat, Math.min(width - minimumWorkspace, pixels)) / width;
    apply();
  }
  function flushDrag() {
    cancelAnimationFrame(frame); frame = 0;
    if (drag) setWidth(drag.x - layout.getBoundingClientRect().left - drag.offset);
  }
  function finish(commit) {
    if (!drag) return;
    if (commit) flushDrag();
    else { cancelAnimationFrame(frame); frame = 0; ratio = drag.initialRatio; }
    const pointer = drag.pointer; drag = undefined;
    if (handle.hasPointerCapture(pointer)) handle.releasePointerCapture(pointer);
    document.documentElement.classList.remove('is-resizing-columns');
    apply(); if (commit) save();
  }
  function reset() { finish(false); ratio = DEFAULT_RATIO; apply(); save(); }
  handle.addEventListener('pointerdown', event => {
    if (!desktop.matches || event.button !== 0 || !event.isPrimary || drag) return;
    event.preventDefault(); handle.focus({ preventScroll: true });
    const boundary = layout.querySelector('.conversation').getBoundingClientRect().right;
    drag = { pointer: event.pointerId, initialRatio: ratio, x: event.clientX, offset: event.clientX - boundary };
    handle.setPointerCapture(event.pointerId);
    document.documentElement.classList.add('is-resizing-columns');
  });
  handle.addEventListener('pointermove', event => {
    if (!drag || event.pointerId !== drag.pointer) return;
    drag.x = event.clientX;
    if (!frame) frame = requestAnimationFrame(flushDrag);
  });
  handle.addEventListener('pointerup', event => {
    if (!drag || event.pointerId !== drag.pointer) return;
    drag.x = event.clientX; finish(true);
  });
  handle.addEventListener('pointercancel', () => finish(false));
  handle.addEventListener('lostpointercapture', () => finish(false));
  handle.addEventListener('dblclick', reset);
  handle.addEventListener('keydown', event => {
    if (!desktop.matches) return;
    if (event.key === 'Escape' && drag) { event.preventDefault(); finish(false); return; }
    if (!['ArrowLeft', 'ArrowRight', 'Home', 'End', 'Enter'].includes(event.key)) return;
    event.preventDefault();
    if (event.key === 'Enter') { reset(); return; }
    finish(false);
    const width = layout.querySelector('.conversation').getBoundingClientRect().width;
    const step = event.shiftKey ? 48 : 16;
    setWidth(event.key === 'Home' ? 0 : event.key === 'End' ? Infinity : width + (event.key === 'ArrowLeft' ? -step : step));
    save();
  });
  window.addEventListener('blur', () => finish(false));
  desktop.addEventListener('change', () => { finish(false); apply(); });
  // Keep the preferred ratio on window resize, clamping only its displayed size.
  new ResizeObserver(apply).observe(layout);
  apply();
}