{valuesToTimeLabel(
params.section.start_time,
diff --git a/apps/web/src/lib/components/recal/left/Handlebar.svelte b/apps/web/src/lib/components/recal/left/Handlebar.svelte
index fddbae5a..cda5db41 100644
--- a/apps/web/src/lib/components/recal/left/Handlebar.svelte
+++ b/apps/web/src/lib/components/recal/left/Handlebar.svelte
@@ -16,6 +16,11 @@
handlebarEl.setPointerCapture(e.pointerId);
document.body.style.cursor = "row-resize";
document.body.style.userSelect = "none";
+
+ // Add window-level listeners to ensure cleanup even if pointer leaves element/window
+ window.addEventListener("pointermove", handlePointerMove);
+ window.addEventListener("pointerup", handlePointerUp);
+ window.addEventListener("pointercancel", handlePointerUp);
}
function handlePointerMove(e: PointerEvent) {
@@ -38,7 +43,21 @@
function handlePointerUp(e: PointerEvent) {
if (!isDragging) return;
isDragging = false;
- handlebarEl.releasePointerCapture(e.pointerId);
+
+ // Clean up window-level listeners
+ window.removeEventListener("pointermove", handlePointerMove);
+ window.removeEventListener("pointerup", handlePointerUp);
+ window.removeEventListener("pointercancel", handlePointerUp);
+
+ // Release pointer capture if we still have it
+ if (handlebarEl && e.pointerId !== undefined) {
+ try {
+ handlebarEl.releasePointerCapture(e.pointerId);
+ } catch {
+ // Pointer capture may already be released
+ }
+ }
+
document.body.style.cursor = "";
document.body.style.userSelect = "";
}
@@ -57,9 +76,6 @@
if (!isDragging) isHovering = false;
}}
on:pointerdown={handlePointerDown}
- on:pointermove={handlePointerMove}
- on:pointerup={handlePointerUp}
- on:pointercancel={handlePointerUp}
on:dblclick={handleDoubleClick}>
diff --git a/apps/web/src/lib/components/ui/SidePanel.svelte b/apps/web/src/lib/components/ui/SidePanel.svelte
new file mode 100644
index 00000000..845f6a1f
--- /dev/null
+++ b/apps/web/src/lib/components/ui/SidePanel.svelte
@@ -0,0 +1,71 @@
+
+
+{#if open}
+
+
+
+
+
+
+{/if}
diff --git a/apps/web/src/lib/scripts/ReCal+/palettes.ts b/apps/web/src/lib/scripts/ReCal+/palettes.ts
index b2a79d87..c912eb2c 100644
--- a/apps/web/src/lib/scripts/ReCal+/palettes.ts
+++ b/apps/web/src/lib/scripts/ReCal+/palettes.ts
@@ -136,15 +136,26 @@ export const colorPalettes: Record
= {
"6": "#b61f4c",
"E": "#2e0812"
},
+ Pixel: {
+ "-1": "#A8A8A8",
+ "0": "#8a004d",
+ "1": "#005c99",
+ "2": "#007040",
+ "3": "#99005c",
+ "4": "#00668a",
+ "5": "#008050",
+ "6": "#a3005c",
+ "E": "#1a1a2e"
+ },
Midnight: {
"-1": "#A8A8A8",
- "0": "#295270",
- "1": "#304f71",
- "2": "#374c72",
- "3": "#3e4a73",
- "4": "#444773",
- "5": "#4b4474",
- "6": "#524175",
+ "0": "#1a5568",
+ "1": "#1a4a5a",
+ "2": "#255060",
+ "3": "#2a4578",
+ "4": "#3a3a72",
+ "5": "#453580",
+ "6": "#503570",
"E": "#1a3246"
},
Cobalt: {
diff --git a/apps/web/src/lib/stores/panel.ts b/apps/web/src/lib/stores/panel.ts
new file mode 100644
index 00000000..f9aa1f88
--- /dev/null
+++ b/apps/web/src/lib/stores/panel.ts
@@ -0,0 +1,19 @@
+/**
+ * @file Store for managing side panel state
+ */
+
+import { writable } from "svelte/store";
+
+export type PanelType = "theme" | null;
+
+function createPanelStore() {
+ const { subscribe, set } = writable(null);
+
+ return {
+ subscribe,
+ open: (panel: PanelType) => set(panel),
+ close: () => set(null)
+ };
+}
+
+export const panelStore = createPanelStore();
diff --git a/apps/web/src/routes/(apps)/+layout.svelte b/apps/web/src/routes/(apps)/+layout.svelte
index 5a2f743d..b1bb5124 100644
--- a/apps/web/src/routes/(apps)/+layout.svelte
+++ b/apps/web/src/routes/(apps)/+layout.svelte
@@ -1,6 +1,7 @@
+