@sevenfold/setto-client 0.2.2 → 0.2.6

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/README.md CHANGED
@@ -137,7 +137,7 @@ The `/admin` dashboard does **not** activate edit mode. Use **Begynn å redigere
137
137
  |--------|-----|
138
138
  | Edit text | Click any `<T>` element — it becomes `contentEditable` |
139
139
  | Edit section colours | Click a section or block background (not text) |
140
- | Follow a link | Ctrl/Cmd + click on nav links |
140
+ | Follow a link | Ctrl/Cmd + click (desktop) · **Naviger ↗** chip when focused (touch) |
141
141
  | Publish | Top toolbar → **Publiser** |
142
142
  | Exit | Top toolbar → **Avslutt** (removes `?setto=edit`) |
143
143
 
@@ -0,0 +1,11 @@
1
+ import { type RefObject } from 'react';
2
+ interface LinkActionChipProps {
3
+ anchorRef: RefObject<HTMLElement | null>;
4
+ visible: boolean;
5
+ }
6
+ /**
7
+ * Touch-only chip shown when editable link/button text is focused.
8
+ * Replaces Ctrl/Cmd+click on coarse-pointer devices.
9
+ */
10
+ export declare function LinkActionChip({ anchorRef, visible }: LinkActionChipProps): import("react").ReactPortal | null;
11
+ export {};
@@ -0,0 +1,4 @@
1
+ /** Editable text inside a navigation target (link or button), excluding language toggles. */
2
+ export declare function isLinkContext(el: HTMLElement): boolean;
3
+ /** Same as Ctrl/Cmd+click — follow the parent link or trigger the parent button. */
4
+ export declare function activateLinkContext(el: HTMLElement): void;
@@ -0,0 +1,2 @@
1
+ /** Place the text caret at viewport coordinates inside a contentEditable element. */
2
+ export declare function placeCaretAtPoint(x: number, y: number, el: HTMLElement): void;
@@ -0,0 +1,2 @@
1
+ /** True on touch-first devices (phones, tablets without hover). */
2
+ export declare function useCoarsePointer(): boolean;
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
- import { useEffect, useState, useRef, useLayoutEffect, createContext, useContext, useCallback, useMemo, useId, useSyncExternalStore, forwardRef } from "react";
2
+ import { useEffect, useSyncExternalStore, useState, useRef, useLayoutEffect, createContext, useContext, useCallback, useMemo, useId, forwardRef } from "react";
3
3
  import { useTranslation } from "react-i18next";
4
4
  import { createPortal } from "react-dom";
5
5
  function __rest(s, e) {
@@ -20987,6 +20987,7 @@ function useSettoDocumentLayout(active) {
20987
20987
  html.setto-edit-mode #root {
20988
20988
  width: 100%;
20989
20989
  min-height: calc(100dvh - var(--setto-toolbar-height));
20990
+ /* Containing block for position:fixed descendants so they sit below the toolbar. */
20990
20991
  transform: translateZ(0);
20991
20992
  }
20992
20993
 
@@ -21006,6 +21007,10 @@ function useSettoDocumentLayout(active) {
21006
21007
  z-index: 2147483645;
21007
21008
  }
21008
21009
 
21010
+ html.setto-edit-mode #setto-hint-root [data-setto-ui] {
21011
+ pointer-events: auto;
21012
+ }
21013
+
21009
21014
  html.setto-edit-mode #setto-section-toolbar-root {
21010
21015
  position: fixed;
21011
21016
  inset: 0;
@@ -21049,43 +21054,68 @@ function notifyLayoutChange() {
21049
21054
  window.dispatchEvent(new Event("resize"));
21050
21055
  });
21051
21056
  }
21052
- const MARGIN$1 = 10;
21053
- const OFFSET = 14;
21057
+ function isLinkContext(el) {
21058
+ const key = el.getAttribute("data-setto-key");
21059
+ if (key?.startsWith("lang.")) return false;
21060
+ if (el.closest("[data-setto-no-hint]")) return false;
21061
+ return !!(el.closest("a[href]") || el.closest("button"));
21062
+ }
21063
+ function activateLinkContext(el) {
21064
+ const anchor = el.closest("a[href]");
21065
+ if (anchor instanceof HTMLAnchorElement) {
21066
+ anchor.click();
21067
+ return;
21068
+ }
21069
+ const button = el.closest("button");
21070
+ if (button instanceof HTMLButtonElement) {
21071
+ button.click();
21072
+ }
21073
+ }
21074
+ const QUERY = "(hover: none) and (pointer: coarse)";
21075
+ function subscribe(onChange) {
21076
+ const mq = window.matchMedia(QUERY);
21077
+ mq.addEventListener("change", onChange);
21078
+ return () => mq.removeEventListener("change", onChange);
21079
+ }
21080
+ function getSnapshot() {
21081
+ return window.matchMedia(QUERY).matches;
21082
+ }
21083
+ function useCoarsePointer() {
21084
+ return useSyncExternalStore(subscribe, getSnapshot, () => false);
21085
+ }
21086
+ const MARGIN$2 = 10;
21087
+ const OFFSET$1 = 14;
21054
21088
  function hintLabel() {
21055
21089
  const isMac = navigator.platform.toUpperCase().includes("MAC");
21056
21090
  return isMac ? "⌘+klikk for å åpne" : "Ctrl+klikk for å åpne";
21057
21091
  }
21058
- function isActionContext(editable) {
21059
- const key = editable.getAttribute("data-setto-key");
21060
- if (key?.startsWith("lang.")) return false;
21061
- if (editable.closest("[data-setto-no-hint]")) return false;
21062
- return !!(editable.closest("a[href]") || editable.closest("button"));
21063
- }
21064
21092
  function placeTooltip(anchor, width, height) {
21065
21093
  const vw = window.innerWidth;
21066
21094
  const vh = window.innerHeight;
21067
- let left = anchor.x + OFFSET;
21068
- let top = anchor.y + OFFSET;
21069
- if (left + width > vw - MARGIN$1) {
21070
- left = anchor.x - width - OFFSET;
21095
+ let left = anchor.x + OFFSET$1;
21096
+ let top = anchor.y + OFFSET$1;
21097
+ if (left + width > vw - MARGIN$2) {
21098
+ left = anchor.x - width - OFFSET$1;
21071
21099
  }
21072
- if (left < MARGIN$1) {
21073
- left = MARGIN$1;
21100
+ if (left < MARGIN$2) {
21101
+ left = MARGIN$2;
21074
21102
  }
21075
- if (top + height > vh - MARGIN$1) {
21076
- top = anchor.y - height - OFFSET;
21103
+ if (top + height > vh - MARGIN$2) {
21104
+ top = anchor.y - height - OFFSET$1;
21077
21105
  }
21078
- if (top < MARGIN$1) {
21079
- top = MARGIN$1;
21106
+ if (top < MARGIN$2) {
21107
+ top = MARGIN$2;
21080
21108
  }
21081
21109
  return { left, top };
21082
21110
  }
21083
21111
  function EditLinkHint() {
21112
+ const coarsePointer = useCoarsePointer();
21084
21113
  const [anchor, setAnchor] = useState(null);
21085
21114
  const [position, setPosition] = useState(null);
21086
21115
  const tipRef = useRef(null);
21087
21116
  const text = hintLabel();
21088
21117
  useEffect(() => {
21118
+ if (coarsePointer) return;
21089
21119
  const onMove = (e) => {
21090
21120
  const target = e.target;
21091
21121
  if (!(target instanceof Element)) {
@@ -21093,7 +21123,7 @@ function EditLinkHint() {
21093
21123
  return;
21094
21124
  }
21095
21125
  const editable = target.closest("[data-setto-key]");
21096
- if (!(editable instanceof HTMLElement) || !isActionContext(editable)) {
21126
+ if (!(editable instanceof HTMLElement) || !isLinkContext(editable)) {
21097
21127
  setAnchor(null);
21098
21128
  return;
21099
21129
  }
@@ -21101,7 +21131,7 @@ function EditLinkHint() {
21101
21131
  };
21102
21132
  document.addEventListener("mousemove", onMove, true);
21103
21133
  return () => document.removeEventListener("mousemove", onMove, true);
21104
- }, []);
21134
+ }, [coarsePointer]);
21105
21135
  useLayoutEffect(() => {
21106
21136
  if (!anchor || !tipRef.current) {
21107
21137
  setPosition(null);
@@ -21110,11 +21140,11 @@ function EditLinkHint() {
21110
21140
  const { width, height } = tipRef.current.getBoundingClientRect();
21111
21141
  setPosition(placeTooltip(anchor, width, height));
21112
21142
  }, [anchor, text]);
21113
- if (!anchor) return null;
21143
+ if (coarsePointer || !anchor) return null;
21114
21144
  const style = {
21115
21145
  position: "fixed",
21116
- left: position?.left ?? anchor.x + OFFSET,
21117
- top: position?.top ?? anchor.y + OFFSET,
21146
+ left: position?.left ?? anchor.x + OFFSET$1,
21147
+ top: position?.top ?? anchor.y + OFFSET$1,
21118
21148
  visibility: position ? "visible" : "hidden",
21119
21149
  background: "#1a1a1a",
21120
21150
  color: "#fff",
@@ -21176,11 +21206,11 @@ function useSectionEdit() {
21176
21206
  }
21177
21207
  const SECTION_TOOLBAR_HEIGHT = 44;
21178
21208
  const GAP = 8;
21179
- const MARGIN = 8;
21209
+ const MARGIN$1 = 8;
21180
21210
  function computePosition(targetEl, toolbarW, toolbarH) {
21181
21211
  const rect = targetEl.getBoundingClientRect();
21182
21212
  let left = rect.left + (rect.width - toolbarW) / 2;
21183
- left = Math.max(MARGIN, Math.min(left, window.innerWidth - toolbarW - MARGIN));
21213
+ left = Math.max(MARGIN$1, Math.min(left, window.innerWidth - toolbarW - MARGIN$1));
21184
21214
  const minTop = TOOLBAR_HEIGHT + GAP;
21185
21215
  const top = Math.max(minTop, rect.top - toolbarH - GAP);
21186
21216
  return { top, left };
@@ -21434,7 +21464,7 @@ function SectionToolbar() {
21434
21464
  top: pos?.top ?? -9999,
21435
21465
  left: pos?.left ?? 0,
21436
21466
  width: "max-content",
21437
- maxWidth: `calc(100vw - ${MARGIN * 2}px)`,
21467
+ maxWidth: `calc(100vw - ${MARGIN$1 * 2}px)`,
21438
21468
  minHeight: SECTION_TOOLBAR_HEIGHT,
21439
21469
  zIndex: 2147483646,
21440
21470
  display: "flex",
@@ -22442,17 +22472,127 @@ function useSetto() {
22442
22472
  }
22443
22473
  return ctx;
22444
22474
  }
22445
- function isLinkContext(el) {
22446
- const key = el.getAttribute("data-setto-key");
22447
- if (key?.startsWith("lang.")) return false;
22448
- if (el.closest("[data-setto-no-hint]")) return false;
22449
- return !!(el.closest("a[href]") || el.closest("button"));
22475
+ const MARGIN = 10;
22476
+ const OFFSET = 8;
22477
+ const MIN_TOUCH = 44;
22478
+ function placeChip(anchor, width, height) {
22479
+ const vw = window.innerWidth;
22480
+ const vh = window.innerHeight;
22481
+ let left = anchor.left + anchor.width / 2 - width / 2;
22482
+ let top = anchor.bottom + OFFSET;
22483
+ if (left + width > vw - MARGIN) left = vw - width - MARGIN;
22484
+ if (left < MARGIN) left = MARGIN;
22485
+ if (top + height > vh - MARGIN) {
22486
+ top = anchor.top - height - OFFSET;
22487
+ }
22488
+ if (top < MARGIN) top = MARGIN;
22489
+ return { left, top };
22490
+ }
22491
+ function LinkActionChip({ anchorRef, visible }) {
22492
+ const chipRef = useRef(null);
22493
+ const [position, setPosition] = useState(null);
22494
+ useLayoutEffect(() => {
22495
+ if (!visible || !anchorRef.current) {
22496
+ setPosition(null);
22497
+ return;
22498
+ }
22499
+ const update = () => {
22500
+ const el = anchorRef.current;
22501
+ const chip = chipRef.current;
22502
+ if (!el) return;
22503
+ const rect = el.getBoundingClientRect();
22504
+ const width = chip?.offsetWidth ?? 120;
22505
+ const height = chip?.offsetHeight ?? MIN_TOUCH;
22506
+ setPosition(placeChip(rect, width, height));
22507
+ };
22508
+ update();
22509
+ window.addEventListener("scroll", update, true);
22510
+ window.addEventListener("resize", update);
22511
+ return () => {
22512
+ window.removeEventListener("scroll", update, true);
22513
+ window.removeEventListener("resize", update);
22514
+ };
22515
+ }, [visible, anchorRef]);
22516
+ if (!visible || !anchorRef.current) return null;
22517
+ const host = document.getElementById("setto-hint-root") ?? document.body;
22518
+ const style = {
22519
+ position: "fixed",
22520
+ left: position?.left ?? 0,
22521
+ top: position?.top ?? 0,
22522
+ visibility: position ? "visible" : "hidden",
22523
+ minHeight: MIN_TOUCH,
22524
+ minWidth: MIN_TOUCH,
22525
+ padding: "10px 14px",
22526
+ background: "#1a1a1a",
22527
+ color: "#fff",
22528
+ fontSize: 13,
22529
+ fontWeight: 500,
22530
+ border: "none",
22531
+ borderRadius: 8,
22532
+ boxShadow: "0 4px 16px rgba(0,0,0,0.2)",
22533
+ fontFamily: "system-ui, sans-serif",
22534
+ whiteSpace: "nowrap",
22535
+ touchAction: "manipulation",
22536
+ zIndex: 2147483647
22537
+ };
22538
+ return createPortal(
22539
+ /* @__PURE__ */ jsx(
22540
+ "button",
22541
+ {
22542
+ ref: chipRef,
22543
+ type: "button",
22544
+ "data-setto-ui": true,
22545
+ style,
22546
+ onMouseDown: (e) => {
22547
+ e.preventDefault();
22548
+ e.stopPropagation();
22549
+ },
22550
+ onClick: (e) => {
22551
+ e.preventDefault();
22552
+ e.stopPropagation();
22553
+ const el = anchorRef.current;
22554
+ if (el) activateLinkContext(el);
22555
+ el?.blur();
22556
+ },
22557
+ children: "Naviger ↗"
22558
+ }
22559
+ ),
22560
+ host
22561
+ );
22562
+ }
22563
+ function placeCaretAtPoint(x, y, el) {
22564
+ el.focus();
22565
+ const doc = el.ownerDocument;
22566
+ const selection = doc.getSelection();
22567
+ if (!selection) return;
22568
+ const range = rangeFromPoint(doc, x, y);
22569
+ if (!range) return;
22570
+ if (!el.contains(range.startContainer)) {
22571
+ range.selectNodeContents(el);
22572
+ range.collapse(false);
22573
+ }
22574
+ selection.removeAllRanges();
22575
+ selection.addRange(range);
22576
+ }
22577
+ function rangeFromPoint(doc, x, y) {
22578
+ if (doc.caretRangeFromPoint) {
22579
+ return doc.caretRangeFromPoint(x, y);
22580
+ }
22581
+ const legacy = doc;
22582
+ const pos = legacy.caretPositionFromPoint?.(x, y);
22583
+ if (!pos) return null;
22584
+ const range = doc.createRange();
22585
+ range.setStart(pos.offsetNode, pos.offset);
22586
+ range.collapse(true);
22587
+ return range;
22450
22588
  }
22451
22589
  function T({ k }) {
22452
22590
  const { t, i18n } = useTranslation();
22453
22591
  const { editMode, store } = useSetto();
22454
22592
  const ref = useRef(null);
22455
22593
  const [focused, setFocused] = useState(false);
22594
+ const [linkContext, setLinkContext] = useState(false);
22595
+ const coarsePointer = useCoarsePointer();
22456
22596
  const [, force] = useState(0);
22457
22597
  useEffect(() => {
22458
22598
  if (!store) return;
@@ -22472,6 +22612,15 @@ function T({ k }) {
22472
22612
  if (e.ctrlKey || e.metaKey) return;
22473
22613
  e.preventDefault();
22474
22614
  };
22615
+ const handleMouseUp = (e) => {
22616
+ if (e.button !== 0) return;
22617
+ e.stopPropagation();
22618
+ const el = e.currentTarget;
22619
+ if (isLinkContext(el) && (e.ctrlKey || e.metaKey)) return;
22620
+ if (isLinkContext(el)) {
22621
+ placeCaretAtPoint(e.clientX, e.clientY, el);
22622
+ }
22623
+ };
22475
22624
  const handleClick = (e) => {
22476
22625
  e.stopPropagation();
22477
22626
  const el = e.currentTarget;
@@ -22479,7 +22628,6 @@ function T({ k }) {
22479
22628
  if (isLinkContext(el)) {
22480
22629
  e.preventDefault();
22481
22630
  }
22482
- el.focus();
22483
22631
  };
22484
22632
  const commit = (el) => {
22485
22633
  const text = el.textContent ?? "";
@@ -22487,37 +22635,47 @@ function T({ k }) {
22487
22635
  };
22488
22636
  const handleBlur = (e) => {
22489
22637
  setFocused(false);
22638
+ setLinkContext(false);
22490
22639
  commit(e.currentTarget);
22491
22640
  };
22492
22641
  const handleInput = (e) => {
22493
22642
  store?.set(k, i18n.language, e.currentTarget.textContent ?? "");
22494
22643
  };
22495
22644
  const lang = toBcp47(i18n.language);
22496
- return /* @__PURE__ */ jsx(
22497
- "span",
22498
- {
22499
- ref,
22500
- "data-setto-key": k,
22501
- contentEditable: true,
22502
- suppressContentEditableWarning: true,
22503
- lang,
22504
- spellCheck: true,
22505
- role: "textbox",
22506
- tabIndex: 0,
22507
- onFocus: () => setFocused(true),
22508
- onBlur: handleBlur,
22509
- onInput: handleInput,
22510
- onMouseDown: handleMouseDown,
22511
- onClick: handleClick,
22512
- style: {
22513
- cursor: "text",
22514
- outline: focused ? "2px solid #640AFF" : void 0,
22515
- outlineOffset: 2,
22516
- borderRadius: 2,
22517
- transition: "outline-color 120ms"
22645
+ const showLinkChip = coarsePointer && focused && linkContext;
22646
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
22647
+ /* @__PURE__ */ jsx(
22648
+ "span",
22649
+ {
22650
+ ref,
22651
+ "data-setto-key": k,
22652
+ contentEditable: true,
22653
+ suppressContentEditableWarning: true,
22654
+ lang,
22655
+ spellCheck: true,
22656
+ role: "textbox",
22657
+ tabIndex: 0,
22658
+ onFocus: (e) => {
22659
+ setFocused(true);
22660
+ setLinkContext(isLinkContext(e.currentTarget));
22661
+ },
22662
+ onBlur: handleBlur,
22663
+ onInput: handleInput,
22664
+ onMouseDown: handleMouseDown,
22665
+ onMouseUp: handleMouseUp,
22666
+ onClick: handleClick,
22667
+ style: {
22668
+ cursor: "text",
22669
+ color: "inherit",
22670
+ outline: focused ? "2px solid #640AFF" : void 0,
22671
+ outlineOffset: 2,
22672
+ borderRadius: 2,
22673
+ transition: "outline-color 120ms"
22674
+ }
22518
22675
  }
22519
- }
22520
- );
22676
+ ),
22677
+ showLinkChip ? /* @__PURE__ */ jsx(LinkActionChip, { anchorRef: ref, visible: focused }) : null
22678
+ ] });
22521
22679
  }
22522
22680
  function toBcp47(code) {
22523
22681
  const base = (code.split("-")[0] ?? code).toLowerCase();
@@ -22661,23 +22819,23 @@ function adminRedirectUrl() {
22661
22819
  }
22662
22820
  function authCallbackType() {
22663
22821
  const hash = window.location.hash.replace(/^#/, "");
22664
- if (hash) {
22665
- const type = new URLSearchParams(hash).get("type");
22666
- if (type === "invite" || type === "recovery") return type;
22667
- }
22668
- const queryType = new URLSearchParams(window.location.search).get("type");
22669
- if (queryType === "invite" || queryType === "recovery") return queryType;
22822
+ if (!hash) return null;
22823
+ const type = new URLSearchParams(hash).get("type");
22824
+ if (type === "invite" || type === "recovery") return type;
22670
22825
  return null;
22671
22826
  }
22672
- function inviteTokenHashFromUrl() {
22673
- return new URLSearchParams(window.location.search).get("token_hash");
22827
+ function clearAuthHashFromUrl() {
22828
+ const { pathname, search } = window.location;
22829
+ window.history.replaceState(null, "", pathname + search);
22674
22830
  }
22675
- function clearAuthParamsFromUrl() {
22676
- const url = new URL(window.location.href);
22677
- url.hash = "";
22678
- url.searchParams.delete("token_hash");
22679
- url.searchParams.delete("type");
22680
- window.history.replaceState(null, "", `${url.pathname}${url.search}`);
22831
+ function consumeInviteErrorFromUrl() {
22832
+ const params = new URLSearchParams(window.location.search);
22833
+ const code = params.get("setto_invite_error");
22834
+ if (!code) return null;
22835
+ params.delete("setto_invite_error");
22836
+ const qs = params.toString();
22837
+ window.history.replaceState(null, "", `${window.location.pathname}${qs ? `?${qs}` : ""}`);
22838
+ return code === "invalid" ? "Invitasjonslenken er ugyldig eller utløpt. Be administratoren om en ny." : "Kunne ikke aktivere invitasjonen.";
22681
22839
  }
22682
22840
  function AuthGate({ children }) {
22683
22841
  const { supabase, session, authLoading } = useSetto();
@@ -22688,38 +22846,22 @@ function AuthGate({ children }) {
22688
22846
  const [error, setError] = useState(null);
22689
22847
  const [info, setInfo] = useState(null);
22690
22848
  const [busy, setBusy] = useState(false);
22691
- const [activatingInvite, setActivatingInvite] = useState(false);
22692
22849
  useEffect(() => {
22693
- const tokenHash = inviteTokenHashFromUrl();
22694
- const callbackType = authCallbackType();
22695
- if (!tokenHash || !callbackType) {
22696
- if (callbackType) setMode("set_password");
22850
+ const inviteError = consumeInviteErrorFromUrl();
22851
+ if (inviteError) {
22852
+ setError(inviteError);
22853
+ setMode("signin");
22697
22854
  return;
22698
22855
  }
22699
- let cancelled = false;
22700
- setActivatingInvite(true);
22701
- void supabase.auth.verifyOtp({ token_hash: tokenHash, type: callbackType }).then(({ error: verifyError }) => {
22702
- if (cancelled) return;
22703
- clearAuthParamsFromUrl();
22704
- if (verifyError) {
22705
- setError(verifyError.message);
22706
- setMode("signin");
22707
- return;
22708
- }
22709
- setMode("set_password");
22710
- }).finally(() => {
22711
- if (!cancelled) setActivatingInvite(false);
22712
- });
22713
- return () => {
22714
- cancelled = true;
22715
- };
22716
- }, [supabase]);
22856
+ const callback = authCallbackType();
22857
+ if (callback) setMode("set_password");
22858
+ }, []);
22717
22859
  if (authLoading) {
22718
22860
  return /* @__PURE__ */ jsx("div", { style: loadingStyle, children: "Laster …" });
22719
22861
  }
22720
22862
  if (session && mode !== "set_password") return /* @__PURE__ */ jsx(Fragment, { children });
22721
- if (mode === "set_password" && !session || activatingInvite) {
22722
- return /* @__PURE__ */ jsx("div", { style: loadingStyle, children: activatingInvite || authCallbackType() ? "Aktiverer invitasjon …" : "Åpne lenken fra e-posten for å sette passord." });
22863
+ if (mode === "set_password" && !session) {
22864
+ return /* @__PURE__ */ jsx("div", { style: loadingStyle, children: authCallbackType() ? "Aktiverer invitasjon …" : "Åpne lenken fra e-posten for å sette passord." });
22723
22865
  }
22724
22866
  const submitSignIn = async (e) => {
22725
22867
  e.preventDefault();
@@ -22774,7 +22916,7 @@ function AuthGate({ children }) {
22774
22916
  try {
22775
22917
  const { error: updateError } = await supabase.auth.updateUser({ password });
22776
22918
  if (updateError) throw updateError;
22777
- clearAuthParamsFromUrl();
22919
+ clearAuthHashFromUrl();
22778
22920
  const { data: sessionData } = await supabase.auth.getSession();
22779
22921
  if (sessionData.session) {
22780
22922
  window.location.replace(editModeUrl());