@satanwagen/reviewkit 0.1.2 → 0.1.3

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +105 -0
  2. package/README.md +134 -63
  3. package/dist/activate.cjs +150 -0
  4. package/dist/activate.cjs.map +1 -0
  5. package/dist/activate.d.cts +59 -0
  6. package/dist/activate.d.ts +59 -0
  7. package/dist/activate.js +21 -0
  8. package/dist/activate.js.map +1 -0
  9. package/dist/{chunk-4YNLMSCK.js → chunk-DG6O5XIC.js} +36 -10
  10. package/dist/chunk-DG6O5XIC.js.map +1 -0
  11. package/dist/chunk-ETAGGIDN.js +119 -0
  12. package/dist/chunk-ETAGGIDN.js.map +1 -0
  13. package/dist/{chunk-YWBFAV57.js → chunk-NSXRFM75.js} +853 -1544
  14. package/dist/chunk-NSXRFM75.js.map +1 -0
  15. package/dist/chunk-Q7U43PXE.js +36 -0
  16. package/dist/chunk-Q7U43PXE.js.map +1 -0
  17. package/dist/client/index.cjs +4530 -4237
  18. package/dist/client/index.cjs.map +1 -1
  19. package/dist/client/index.d.cts +1 -1
  20. package/dist/client/index.d.ts +1 -1
  21. package/dist/client/index.js +49 -3
  22. package/dist/client/index.js.map +1 -1
  23. package/dist/effects-EWKHKUDP.js +851 -0
  24. package/dist/effects-EWKHKUDP.js.map +1 -0
  25. package/dist/index-BUSmAweg.d.ts +77 -0
  26. package/dist/index-Dl2Fl0qr.d.cts +77 -0
  27. package/dist/index.cjs +4544 -4256
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +35 -3
  32. package/dist/index.js.map +1 -1
  33. package/dist/lazy.cjs +8988 -0
  34. package/dist/lazy.cjs.map +1 -0
  35. package/dist/lazy.d.cts +7 -0
  36. package/dist/lazy.d.ts +7 -0
  37. package/dist/lazy.js +8 -0
  38. package/dist/lazy.js.map +1 -0
  39. package/dist/next.cjs +8989 -0
  40. package/dist/next.cjs.map +1 -0
  41. package/dist/next.d.cts +4 -0
  42. package/dist/next.d.ts +4 -0
  43. package/dist/next.js +9 -0
  44. package/dist/next.js.map +1 -0
  45. package/dist/schema.cjs +53 -16
  46. package/dist/schema.cjs.map +1 -1
  47. package/dist/schema.d.cts +20 -5
  48. package/dist/schema.d.ts +20 -5
  49. package/dist/schema.js +30 -2
  50. package/dist/schema.js.map +1 -1
  51. package/package.json +21 -8
  52. package/cli/emblema-sync.mjs +0 -675
  53. package/dist/chunk-4YNLMSCK.js.map +0 -1
  54. package/dist/chunk-YWBFAV57.js.map +0 -1
  55. package/dist/index-BbucFgZi.d.ts +0 -49
  56. package/dist/index-CBlJrKkm.d.cts +0 -49
@@ -1,7 +1,30 @@
1
- import { parseSessionJson, isCopyItem, isImageItem, sendToEmblema, parseSession, SCHEMA_VERSION, pingEmblema, isKnownItemType, fetchSyncKey, revokeSyncKey, isCommentItem, isDeleteItem, isMoveItem, isStyleItem, fetchSyncState, generateSyncKey, ITEM_STATUSES } from './chunk-4YNLMSCK.js';
2
- import { createContext, useState, useEffect, useReducer, useMemo, useRef, useContext, useLayoutEffect } from 'react';
3
- import { createPortal } from 'react-dom';
4
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
1
+ import {
2
+ ITEM_STATUSES,
3
+ SCHEMA_VERSION,
4
+ fetchSyncKey,
5
+ fetchSyncState,
6
+ generateSyncKey,
7
+ isCommentItem,
8
+ isCopyItem,
9
+ isDeleteItem,
10
+ isImageItem,
11
+ isKnownItemType,
12
+ isMoveItem,
13
+ isStyleItem,
14
+ parseSession,
15
+ parseSessionJson,
16
+ pingEmblema,
17
+ revokeSyncKey,
18
+ sendToEmblema
19
+ } from "./chunk-DG6O5XIC.js";
20
+ import {
21
+ clearSticky,
22
+ isLocalHost,
23
+ isStickyEnabled,
24
+ reviewParam,
25
+ setSticky,
26
+ shouldActivate
27
+ } from "./chunk-ETAGGIDN.js";
5
28
 
6
29
  // src/client/target.ts
7
30
  var HOST_ATTR = "data-review-kit-host";
@@ -316,19 +339,14 @@ function importSessionFile(file) {
316
339
  });
317
340
  }
318
341
 
342
+ // src/client/ReviewKit.tsx
343
+ import { useEffect as useEffect17, useState as useState13 } from "react";
344
+ import { createPortal } from "react-dom";
345
+
319
346
  // src/client/env.ts
320
- var STICKY_KEY = "review-kit:sticky";
321
347
  var LEGACY_SESSION_KEY = "review-kit:active";
322
348
  var LEGACY_LOCAL_FLAG = "reviewkit";
323
- var STICKY_TTL_MS = 4 * 60 * 60 * 1e3;
324
349
  var DEACTIVATE_EVENT = "review-kit:deactivate";
325
- function isDevBuild() {
326
- try {
327
- return process.env.NODE_ENV === "development";
328
- } catch {
329
- return false;
330
- }
331
- }
332
350
  function safeRemove(storage, key) {
333
351
  try {
334
352
  storage().removeItem(key);
@@ -339,40 +357,8 @@ function clearLegacyKeys() {
339
357
  safeRemove(() => window.sessionStorage, LEGACY_SESSION_KEY);
340
358
  safeRemove(() => window.localStorage, LEGACY_LOCAL_FLAG);
341
359
  }
342
- function readSticky() {
343
- try {
344
- const raw = window.sessionStorage.getItem(STICKY_KEY);
345
- if (!raw) return null;
346
- const parsed = JSON.parse(raw);
347
- if (typeof parsed === "object" && parsed !== null && typeof parsed.token === "string" && typeof parsed.until === "number") {
348
- return parsed;
349
- }
350
- } catch {
351
- }
352
- return null;
353
- }
354
- function isStickyEnabled(token) {
355
- const sticky = readSticky();
356
- if (!sticky) return false;
357
- if (sticky.until < Date.now() || sticky.token !== (token ?? "")) {
358
- safeRemove(() => window.sessionStorage, STICKY_KEY);
359
- return false;
360
- }
361
- return true;
362
- }
363
- function setSticky(token, on) {
364
- try {
365
- if (on) {
366
- const value = { token: token ?? "", until: Date.now() + STICKY_TTL_MS };
367
- window.sessionStorage.setItem(STICKY_KEY, JSON.stringify(value));
368
- } else {
369
- window.sessionStorage.removeItem(STICKY_KEY);
370
- }
371
- } catch {
372
- }
373
- }
374
360
  function deactivateReview() {
375
- safeRemove(() => window.sessionStorage, STICKY_KEY);
361
+ clearSticky();
376
362
  clearLegacyKeys();
377
363
  try {
378
364
  const url = new URL(window.location.href);
@@ -387,67 +373,24 @@ function deactivateReview() {
387
373
  } catch {
388
374
  }
389
375
  }
390
- function hostAllowed(allowedHosts) {
391
- if (!allowedHosts || allowedHosts.length === 0) return true;
392
- let host = "";
393
- try {
394
- host = window.location.hostname.toLowerCase();
395
- } catch {
396
- return false;
397
- }
398
- return allowedHosts.some((pattern) => {
399
- const regex = new RegExp(
400
- "^" + pattern.toLowerCase().split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
401
- );
402
- return regex.test(host);
403
- });
404
- }
405
- function resolveActivation(token) {
376
+ var warnedNoAllowlist = false;
377
+ function resolveActivation(options) {
406
378
  if (typeof window === "undefined") return false;
407
- let param = null;
408
- try {
409
- param = new URLSearchParams(window.location.search).get("review");
410
- } catch {
411
- param = null;
412
- }
413
- if (param === "off") {
414
- safeRemove(() => window.sessionStorage, STICKY_KEY);
415
- clearLegacyKeys();
416
- return false;
417
- }
418
- const tokenConfigured = token !== void 0 && token !== "";
419
- if (tokenConfigured) {
420
- if (param !== null && param !== "") {
421
- if (param === token) {
422
- setSticky(token, true);
423
- return true;
424
- }
425
- console.warn("[review-kit] `?review` token does not match the configured token; staying inactive.");
426
- return false;
427
- }
428
- if (isStickyEnabled(token)) {
429
- setSticky(token, true);
430
- return true;
379
+ clearLegacyKeys();
380
+ const active = shouldActivate(options);
381
+ if (!active) return false;
382
+ const token = options.token;
383
+ const param = reviewParam();
384
+ if (token !== void 0 && token !== "" && (param === token || isStickyEnabled(token))) {
385
+ setSticky(token, true);
386
+ if (param === token && !options.allowedHosts?.length && !isLocalHost() && !warnedNoAllowlist) {
387
+ warnedNoAllowlist = true;
388
+ console.warn(
389
+ "[review-kit] Review mode activated by URL token on a public hostname with no `allowedHosts`. For production pass `allowedHosts` next to `token` so the layer cannot be opened on hosts you did not intend."
390
+ );
431
391
  }
432
- clearLegacyKeys();
433
- return false;
434
392
  }
435
- if (param !== null && param !== "") {
436
- console.warn(
437
- "[review-kit] Activated via `?review` with no token configured. Pass a `token` prop to <ReviewKit /> to restrict who can open review mode."
438
- );
439
- setSticky(void 0, true);
440
- return true;
441
- }
442
- if (isStickyEnabled(void 0)) {
443
- setSticky(void 0, true);
444
- return true;
445
- }
446
- try {
447
- if (window.localStorage.getItem(LEGACY_LOCAL_FLAG) !== null) return true;
448
- } catch {
449
- }
450
- return isDevBuild();
393
+ return true;
451
394
  }
452
395
  var FOCUS_KEY = "review-kit:focus";
453
396
  function rememberFocusItem(id) {
@@ -1295,6 +1238,7 @@ input, textarea, select {
1295
1238
  .rk-epush-ok .rk-sync-dot { background: var(--rk-success); }
1296
1239
  .rk-epush-sending .rk-sync-dot { background: var(--rk-warn); }
1297
1240
  .rk-epush-offline .rk-sync-dot { background: var(--rk-fg-faint); }
1241
+ .rk-epush-rejected .rk-sync-dot { background: var(--rk-danger, #d33); }
1298
1242
 
1299
1243
  /* ---------------------------------------------------------------- */
1300
1244
  /* Floating editor cards */
@@ -2280,6 +2224,9 @@ input, textarea, select {
2280
2224
  `;
2281
2225
  }
2282
2226
 
2227
+ // src/client/store.tsx
2228
+ import { createContext, useContext, useMemo, useReducer } from "react";
2229
+
2283
2230
  // src/client/id.ts
2284
2231
  function rkId() {
2285
2232
  const time = Date.now().toString(36);
@@ -2295,6 +2242,9 @@ function rkId() {
2295
2242
  }
2296
2243
  return `rk-${time}-${rand}`;
2297
2244
  }
2245
+
2246
+ // src/client/store.tsx
2247
+ import { jsx } from "react/jsx-runtime";
2298
2248
  function itemMatchesFilter(item, filter) {
2299
2249
  if (!filter) return true;
2300
2250
  if (filter.types.length > 0 && !filter.types.includes(item.type)) return false;
@@ -2617,7 +2567,7 @@ function StoreProvider({ config, children }) {
2617
2567
  const configValue = useMemo(
2618
2568
  () => config,
2619
2569
  // eslint-disable-next-line react-hooks/exhaustive-deps -- config object identity is per-render; depend on its fields
2620
- [config.token, config.syncToken, config.syncUrl, config.defaultAuthor, config.position, config.storageKey, config.onSessionChange, config.emblema]
2570
+ [config.enabled, config.token, config.devAutoOn, config.syncToken, config.syncUrl, config.defaultAuthor, config.fxAssetsUrl, config.position, config.storageKey, config.onSessionChange, config.emblema]
2621
2571
  );
2622
2572
  return /* @__PURE__ */ jsx(ConfigContext.Provider, { value: configValue, children: /* @__PURE__ */ jsx(StateContext.Provider, { value: state, children: /* @__PURE__ */ jsx(DispatchContext.Provider, { value: dispatch, children }) }) });
2623
2573
  }
@@ -2645,6 +2595,9 @@ function toast(dispatch, message, tone = "info") {
2645
2595
  dispatch({ type: "PUSH_TOAST", toast: { id: rkId(), message, tone } });
2646
2596
  }
2647
2597
 
2598
+ // src/client/App.tsx
2599
+ import { useEffect as useEffect16, useRef as useRef16 } from "react";
2600
+
2648
2601
  // src/client/persist.ts
2649
2602
  var DB_NAME = "review-kit";
2650
2603
  var STORE = "sessions";
@@ -2771,6 +2724,9 @@ async function revealTarget(target) {
2771
2724
  const el = resolveTarget(target)?.element ?? null;
2772
2725
  return el && isElementVisible(el) ? "revealed" : "failed";
2773
2726
  }
2727
+
2728
+ // src/client/preview.ts
2729
+ import { useEffect, useRef } from "react";
2774
2730
  var EDITED_ATTR = "data-rk-edited";
2775
2731
  var STYLE_ID = "rk-preview-style";
2776
2732
  var RESCHEDULE_MS = 180;
@@ -2989,836 +2945,91 @@ function useAppliedPreview(state, dispatch) {
2989
2945
  }, []);
2990
2946
  }
2991
2947
 
2992
- // src/client/effects.ts
2993
- var FX_SOUND_BASE = "https://agropolio.fucking.style/rk-assets";
2994
- var ONDRO_MS = 12e4;
2995
- var BIG_MS = 45e3;
2996
- var NICE_MS = 6900;
2997
- var CHAOS_SEL = "body > :not([data-review-kit-host]):not(script):not(style):not(svg[data-rk-fx])";
2998
- var broadcast = null;
2999
- function setEffectBroadcaster(fn) {
3000
- broadcast = fn;
3001
- }
3002
- var active = /* @__PURE__ */ new Set();
3003
- function cancelEffects() {
3004
- if (active.size === 0) return false;
3005
- for (const cancel of [...active]) cancel();
3006
- return true;
3007
- }
3008
- function reducedMotion() {
3009
- try {
3010
- return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
3011
- } catch {
3012
- return false;
3013
- }
3014
- }
3015
- function spawnParticles(layer, spec, reduced, duration) {
3016
- const count = reduced ? Math.min(10, spec.count) : spec.count;
3017
- const vw = window.innerWidth;
3018
- const vh = window.innerHeight;
3019
- for (let i = 0; i < count; i++) {
3020
- const particle = document.createElement("span");
3021
- particle.className = spec.className ? `rk-fx-particle ${spec.className}` : "rk-fx-particle";
3022
- particle.textContent = spec.glyphs[i % spec.glyphs.length];
3023
- const delay = i / count * duration * 0.4;
3024
- const life = duration * (0.45 + 0.35 * (i * 7 % 10) / 10);
3025
- particle.style.fontSize = `${14 + i * 13 % 18}px`;
3026
- if (spec.kind === "rain") {
3027
- const x = i * 61 % 100 / 100 * vw;
3028
- const sway = i * 31 % 60 - 30 | 0;
3029
- particle.style.setProperty("--fx-x0", `${x}px`);
3030
- particle.style.setProperty("--fx-x1", `${x + sway}px`);
3031
- particle.style.setProperty("--fx-y0", `-40px`);
3032
- particle.style.setProperty("--fx-y1", `${vh + 40}px`);
3033
- } else {
3034
- const angle = i / count * Math.PI * 2;
3035
- const radius = 120 + i * 47 % 240;
3036
- particle.style.setProperty("--fx-x0", `${vw / 2}px`);
3037
- particle.style.setProperty("--fx-y0", `${vh / 2}px`);
3038
- particle.style.setProperty("--fx-x1", `${vw / 2 + Math.cos(angle) * radius}px`);
3039
- particle.style.setProperty("--fx-y1", `${vh / 2 + Math.sin(angle) * radius + vh * 0.4}px`);
3040
- }
3041
- particle.style.animation = `rk-fx-fall ${reduced ? life * 0.8 : life}ms linear ${delay}ms both`;
3042
- layer.appendChild(particle);
3043
- }
3044
- }
3045
- function runShake() {
3046
- const prev = document.body.style.animation;
3047
- let styleTag = document.getElementById("rk-fx-shake-style");
3048
- if (!styleTag) {
3049
- styleTag = document.createElement("style");
3050
- styleTag.id = "rk-fx-shake-style";
3051
- styleTag.textContent = "@keyframes rk-fx-shake { 0%,100% { transform: translate(0,0); } 25% { transform: translate(-5px,3px); } 50% { transform: translate(4px,-4px); } 75% { transform: translate(-3px,-2px); } }";
3052
- document.head.appendChild(styleTag);
3053
- }
3054
- document.body.style.animation = "rk-fx-shake 260ms ease-in-out 6";
3055
- let done = false;
3056
- const restore2 = () => {
3057
- if (done) return;
3058
- done = true;
3059
- document.body.style.animation = prev;
3060
- if (document.body.getAttribute("style") === "") document.body.removeAttribute("style");
3061
- };
3062
- window.setTimeout(restore2, 1700);
3063
- return restore2;
3064
- }
3065
- function startOndro({ layer, reduced }) {
3066
- const cleanups = [];
3067
- const timers = [];
3068
- const intervals = [];
3069
- const later = (fn, ms) => timers.push(window.setTimeout(fn, ms));
3070
- const every = (fn, ms) => intervals.push(window.setInterval(fn, ms));
3071
- cleanups.push(() => {
3072
- timers.forEach((t) => window.clearTimeout(t));
3073
- intervals.forEach((t) => window.clearInterval(t));
3074
- });
3075
- const addStyle = (css) => {
3076
- const style = document.createElement("style");
3077
- style.setAttribute("data-rk-fx", "ondro");
3078
- style.textContent = css;
3079
- document.head.appendChild(style);
3080
- cleanups.push(() => style.remove());
3081
- return style;
3082
- };
3083
- const wave = (spec) => {
3084
- const sub = document.createElement("div");
3085
- sub.className = "rk-fx-layer";
3086
- layer.appendChild(sub);
3087
- spawnParticles(sub, spec, reduced, spec.durationMs);
3088
- const t = window.setTimeout(() => sub.remove(), spec.durationMs + 1600);
3089
- timers.push(t);
3090
- cleanups.push(() => sub.remove());
3091
- };
3092
- if (reduced) {
3093
- addStyle(`@keyframes rk-ondro { 0% { filter: none; } 50% { filter: hue-rotate(40deg) saturate(1.15); } 100% { filter: none; } }
3094
- ${CHAOS_SEL} { animation: rk-ondro 8s ease-in-out infinite; }`);
3095
- wave({ kind: "rain", glyphs: ["\u{1F92A}", "\u{1F4A5}", "\u{1F300}"], count: 8, durationMs: 8e3 });
3096
- every(() => wave({ kind: "rain", glyphs: ["\u{1F92A}", "\u{1F300}"], count: 6, durationMs: 8e3 }), 15e3);
3097
- } else {
3098
- const scrollX = window.scrollX;
3099
- const scrollY = window.scrollY;
3100
- const htmlOverflow = document.documentElement.style.overflow;
3101
- document.documentElement.style.overflow = "hidden";
3102
- cleanups.push(() => {
3103
- document.documentElement.style.overflow = htmlOverflow;
3104
- if (document.documentElement.getAttribute("style") === "") document.documentElement.removeAttribute("style");
3105
- window.scrollTo(scrollX, scrollY);
3106
- });
3107
- const originY = scrollY + window.innerHeight / 2;
3108
- addStyle(`${CHAOS_SEL} { transform-origin: 50vw ${originY}px; }`);
3109
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3110
- svg.setAttribute("data-rk-fx", "ondro");
3111
- svg.setAttribute("width", "0");
3112
- svg.setAttribute("height", "0");
3113
- svg.style.position = "absolute";
3114
- svg.innerHTML = `<defs>
3115
- <filter id="rk-ondro-warp" x="-10%" y="-10%" width="120%" height="120%">
3116
- <feTurbulence type="turbulence" baseFrequency="0.004 0.017" numOctaves="2" result="n" seed="7">
3117
- <animate attributeName="baseFrequency" dur="5.2s" values="0.004 0.017;0.013 0.005;0.004 0.017" repeatCount="indefinite"/>
3118
- </feTurbulence>
3119
- <feDisplacementMap in="SourceGraphic" in2="n" scale="14" xChannelSelector="R" yChannelSelector="G">
3120
- <animate attributeName="scale" dur="4.4s" values="6;26;6" repeatCount="indefinite"/>
3121
- </feDisplacementMap>
3122
- </filter>
3123
- <filter id="rk-ondro-ca" x="-5%" y="-5%" width="110%" height="110%">
3124
- <feColorMatrix in="SourceGraphic" type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="r"/>
3125
- <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="g"/>
3126
- <feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" result="b"/>
3127
- <feOffset in="r" dx="4" dy="0" result="ro"><animate attributeName="dx" dur="2.9s" values="1;6;1" repeatCount="indefinite"/></feOffset>
3128
- <feOffset in="b" dx="-4" dy="1" result="bo"><animate attributeName="dx" dur="3.3s" values="-1;-6;-1" repeatCount="indefinite"/></feOffset>
3129
- <feBlend in="ro" in2="g" mode="screen" result="rg"/>
3130
- <feBlend in="rg" in2="bo" mode="screen"/>
3131
- </filter>
3132
- </defs>`;
3133
- document.body.appendChild(svg);
3134
- cleanups.push(() => svg.remove());
3135
- addStyle(`@keyframes rk-ondro1 {
3136
- 0% { filter: none; transform: scale(1.22); }
3137
- 25% { filter: hue-rotate(120deg) saturate(1.9) blur(1.6px); transform: scale(1.238) rotate(1.4deg) translateX(6px); }
3138
- 50% { filter: hue-rotate(230deg) saturate(0.6) contrast(1.12) blur(0.6px); transform: scale(1.202) rotate(-1.2deg) translateY(5px); }
3139
- 75% { filter: hue-rotate(310deg) saturate(2.1) blur(2.6px); transform: scale(1.22) skewX(1.1deg) translateX(-7px); }
3140
- 100% { filter: hue-rotate(360deg); transform: scale(1.22); }
3141
- }
3142
- ${CHAOS_SEL} { animation: rk-ondro1 2.6s ease-in-out infinite; }`);
3143
- later(() => {
3144
- addStyle(`@keyframes rk-ondro2 {
3145
- 0% { filter: url(#rk-ondro-warp) hue-rotate(0deg) saturate(1.3); transform: scale(1.22); }
3146
- 20% { filter: url(#rk-ondro-ca) hue-rotate(100deg) saturate(2.3) blur(3.4px); transform: scale(1.256) rotate(2.4deg) translate(12px, -6px); }
3147
- 45% { filter: url(#rk-ondro-warp) hue-rotate(190deg) saturate(0.45) contrast(1.2) blur(1.4px); transform: scale(1.19) rotate(-2.2deg) translate(-13px, 8px) skewY(1.2deg); }
3148
- 70% { filter: url(#rk-ondro-ca) hue-rotate(280deg) saturate(2.4) blur(4.6px); transform: scale(1.244) skewX(-1.8deg) translateY(-10px); }
3149
- 100% { filter: url(#rk-ondro-warp) hue-rotate(360deg) saturate(1.3); transform: scale(1.22); }
3150
- }
3151
- ${CHAOS_SEL} { animation: rk-ondro2 2.2s ease-in-out infinite; }
3152
- @keyframes rk-ondro-shake { 0%,100% { transform: translate(0,0); } 20% { transform: translate(-6px,4px); } 40% { transform: translate(5px,-5px); } 60% { transform: translate(-4px,-3px); } 80% { transform: translate(6px,3px); } }
3153
- @keyframes rk-ondro-text {
3154
- 0%,100% { letter-spacing: normal; word-spacing: normal; text-shadow: none; }
3155
- 30% { letter-spacing: 0.12em; word-spacing: 0.3em; text-shadow: 4px 0 rgba(255,0,90,0.6), -4px 1px rgba(0,190,255,0.6); }
3156
- 60% { letter-spacing: -0.05em; word-spacing: -0.1em; text-shadow: -6px 0 rgba(255,0,90,0.55), 6px 2px rgba(0,190,255,0.55), 0 3px rgba(120,255,0,0.4); }
3157
- 80% { letter-spacing: 0.2em; text-shadow: 2px -2px rgba(255,0,90,0.65), -2px 2px rgba(0,190,255,0.65); }
3158
- }
3159
- h1, h2, h3, h4, p, a, li, button, span { animation: rk-ondro-text 3.1s ease-in-out infinite; }`);
3160
- startTextDecay(cleanups, every);
3161
- spawnGlitchBands(layer, cleanups);
3162
- wave({ kind: "burst", glyphs: ["\u{1F4A5}", "\u{1F9E8}", "\u{1F525}", "\u{1F300}"], count: 34, durationMs: 5e3 });
3163
- }, 6e3);
3164
- later(() => {
3165
- addStyle(`@keyframes rk-ondro3 {
3166
- 0% { filter: url(#rk-ondro-warp) hue-rotate(0deg) saturate(1.6) invert(0); transform: scale(1.22); }
3167
- 18% { filter: url(#rk-ondro-ca) hue-rotate(140deg) saturate(2.5) blur(6px) invert(0.35); transform: scale(1.27) rotate(3.2deg) translate(20px, -12px); }
3168
- 38% { filter: url(#rk-ondro-warp) hue-rotate(220deg) saturate(0.4) blur(2px) invert(0.75); transform: scale(1.18) rotate(-3deg) translate(-22px, 12px) skewY(2deg); }
3169
- 58% { filter: url(#rk-ondro-ca) hue-rotate(300deg) saturate(2.2) blur(7px) invert(0.3); transform: scale(1.25) skewX(2.4deg) translateY(-16px); }
3170
- 80% { filter: url(#rk-ondro-warp) hue-rotate(340deg) saturate(1.8) blur(3px) invert(0.05); transform: scale(1.23) rotate(1.6deg) translate(14px, 6px); }
3171
- 100% { filter: url(#rk-ondro-warp) hue-rotate(360deg) saturate(1.6) invert(0); transform: scale(1.22); }
3172
- }
3173
- ${CHAOS_SEL} { animation: rk-ondro3 4s ease-in-out infinite; }
3174
- @keyframes rk-ondro-shake3 { 0%,100% { transform: translate(0,0) rotate(0); } 25% { transform: translate(-9px,6px) rotate(-0.4deg); } 50% { transform: translate(8px,-8px) rotate(0.5deg); } 75% { transform: translate(-7px,-5px) rotate(-0.3deg); } }
3175
- body { animation: rk-ondro-shake3 620ms ease-in-out infinite; }`);
3176
- wave({ kind: "rain", glyphs: ["\u{1F92F}", "\u{1F480}", "\u{1F9E0}", "\u26A1\uFE0F", "\u{1FAE0}", "\u{1F529}"], count: 60, durationMs: 7e3 });
3177
- }, 13e3);
3178
- wave({ kind: "rain", glyphs: ["\u{1F92A}", "\u{1F4A5}", "\u{1F300}", "\u{1F9E0}", "\u26A1\uFE0F"], count: 40, durationMs: 12e3 });
3179
- every(() => wave({ kind: "rain", glyphs: ["\u{1F92A}", "\u{1F4A5}", "\u{1F300}", "\u{1F9E0}", "\u26A1\uFE0F", "\u{1F355}", "\u{1F4C0}"], count: 30, durationMs: 9e3 }), 9500);
3180
- every(() => spawnFallingLetters(layer), 12e3);
3181
- later(() => spawnFallingLetters(layer), 7e3);
3182
- }
3183
- const audio = new Audio(`${FX_SOUND_BASE}/rick.mp3`);
3184
- audio.volume = 0.8;
3185
- audio.loop = true;
3186
- cleanups.push(() => {
3187
- audio.pause();
3188
- audio.src = "";
3189
- });
3190
- audio.play().catch(() => {
3191
- const chip = document.createElement("button");
3192
- chip.className = "rk-fx-sound";
3193
- chip.type = "button";
3194
- chip.textContent = "\u{1F50A} klikni pre zvuk";
3195
- chip.addEventListener("click", () => {
3196
- audio.play().catch(() => void 0);
3197
- chip.remove();
3198
- });
3199
- layer.appendChild(chip);
3200
- cleanups.push(() => chip.remove());
3201
- });
3202
- installKillWord(cleanups);
3203
- return () => {
3204
- for (const fn of cleanups) fn();
3205
- };
2948
+ // src/client/effectCatalog.ts
2949
+ var EFFECT_NAMES = [
2950
+ "ondro",
2951
+ "lava",
2952
+ "sklo",
2953
+ "vir",
2954
+ "crt",
2955
+ "hovno",
2956
+ "konfety",
2957
+ "srdiecka",
2958
+ "sneh",
2959
+ "zemetrasenie",
2960
+ "69",
2961
+ "kokot",
2962
+ "vrtule",
2963
+ "muchy",
2964
+ "pes",
2965
+ "pivo",
2966
+ "nikotin",
2967
+ "matrix",
2968
+ "disko",
2969
+ "zrkadlo",
2970
+ "pohreb",
2971
+ "laser",
2972
+ "kura",
2973
+ "spam"
2974
+ ];
2975
+ var NAME_SET = new Set(EFFECT_NAMES);
2976
+ function isEffectCommand(name) {
2977
+ return name === "stop" || NAME_SET.has(name);
3206
2978
  }
3207
- function installKillWord(cleanups) {
3208
- let buffer = "";
3209
- const inTextField = (event) => event.composedPath().some(
3210
- (node) => node instanceof HTMLElement && (node.tagName === "INPUT" || node.tagName === "TEXTAREA" || node.isContentEditable)
3211
- );
3212
- const KILL_LETTERS = /* @__PURE__ */ new Set(["h", "o", "v", "n"]);
3213
- const onKey = (event) => {
3214
- if (event.key.length !== 1 || event.metaKey || event.ctrlKey || event.altKey) return;
3215
- const key = event.key.toLowerCase();
3216
- buffer = (buffer + key).slice(-8);
3217
- if (KILL_LETTERS.has(key) && !inTextField(event)) event.stopPropagation();
3218
- if (buffer.includes("hovno")) {
3219
- buffer = "";
3220
- broadcast?.("stop");
3221
- cancelEffects();
3222
- }
3223
- };
3224
- document.addEventListener("keydown", onKey, true);
3225
- cleanups.push(() => document.removeEventListener("keydown", onKey, true));
3226
- }
3227
- function bigEffect(build) {
3228
- return function start({ layer, reduced }) {
3229
- const cleanups = [];
3230
- const timers = [];
3231
- const intervals = [];
3232
- const later = (fn, ms) => timers.push(window.setTimeout(fn, ms));
3233
- const every = (fn, ms) => intervals.push(window.setInterval(fn, ms));
3234
- cleanups.push(() => {
3235
- timers.forEach((t) => window.clearTimeout(t));
3236
- intervals.forEach((t) => window.clearInterval(t));
3237
- });
3238
- const addStyle = (css) => {
3239
- const style = document.createElement("style");
3240
- style.setAttribute("data-rk-fx", "");
3241
- style.textContent = css;
3242
- document.head.appendChild(style);
3243
- cleanups.push(() => style.remove());
3244
- };
3245
- const wave = (spec) => {
3246
- const sub = document.createElement("div");
3247
- sub.className = "rk-fx-layer";
3248
- layer.appendChild(sub);
3249
- spawnParticles(sub, spec, reduced, spec.durationMs);
3250
- timers.push(window.setTimeout(() => sub.remove(), spec.durationMs + 1600));
3251
- cleanups.push(() => sub.remove());
3252
- };
3253
- if (!reduced) {
3254
- const scrollX = window.scrollX;
3255
- const scrollY = window.scrollY;
3256
- const htmlOverflow = document.documentElement.style.overflow;
3257
- document.documentElement.style.overflow = "hidden";
3258
- cleanups.push(() => {
3259
- document.documentElement.style.overflow = htmlOverflow;
3260
- if (document.documentElement.getAttribute("style") === "") document.documentElement.removeAttribute("style");
3261
- window.scrollTo(scrollX, scrollY);
3262
- });
3263
- const originY = scrollY + window.innerHeight / 2;
3264
- addStyle(`${CHAOS_SEL} { transform-origin: 50vw ${originY}px; }`);
3265
- }
3266
- build({
3267
- layer,
3268
- reduced,
3269
- addStyle,
3270
- later,
3271
- every,
3272
- wave,
3273
- decayText: () => startTextDecay(cleanups, every),
3274
- cleanups
2979
+
2980
+ // src/client/effectsLazy.ts
2981
+ var loaded = null;
2982
+ var loading = null;
2983
+ var broadcaster = null;
2984
+ var assetsUrl = null;
2985
+ var queued = 0;
2986
+ function load() {
2987
+ if (loaded) return Promise.resolve(loaded);
2988
+ if (!loading) {
2989
+ loading = import("./effects-EWKHKUDP.js").then((mod) => {
2990
+ loaded = mod;
2991
+ mod.setEffectBroadcaster(broadcaster);
2992
+ mod.setFxAssetsUrl(assetsUrl);
2993
+ return mod;
2994
+ }).catch((error) => {
2995
+ loading = null;
2996
+ queued = 0;
2997
+ throw error;
3275
2998
  });
3276
- installKillWord(cleanups);
3277
- return () => {
3278
- for (const fn of cleanups) fn();
3279
- };
3280
- };
3281
- }
3282
- function startTextDecay(cleanups, every) {
3283
- const GLYPHS = "\u2593\u2592\u2591#@%&$?!\xD7\xD8\u2206\xA7";
3284
- const originals = /* @__PURE__ */ new Map();
3285
- const nodes = [];
3286
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
3287
- let node;
3288
- while ((node = walker.nextNode()) && nodes.length < 120) {
3289
- const text = node;
3290
- const value = text.nodeValue ?? "";
3291
- if (value.trim().length < 4) continue;
3292
- const parent = text.parentElement;
3293
- if (!parent || parent.closest("[data-review-kit-host], script, style, [data-rk-edited]")) continue;
3294
- nodes.push(text);
3295
2999
  }
3296
- every(() => {
3297
- for (let i = 0; i < 22; i++) {
3298
- const text = nodes[Math.random() * nodes.length | 0];
3299
- if (!text || !text.isConnected) continue;
3300
- if (!originals.has(text)) originals.set(text, text.nodeValue ?? "");
3301
- const original = originals.get(text) ?? "";
3302
- if (Math.random() < 0.3) {
3303
- text.nodeValue = original;
3304
- continue;
3305
- }
3306
- const chars = [...original];
3307
- const swaps = 1 + (Math.random() * 3 | 0);
3308
- for (let k = 0; k < swaps; k++) {
3309
- const idx = Math.random() * chars.length | 0;
3310
- if (chars[idx] !== " ") chars[idx] = GLYPHS[Math.random() * GLYPHS.length | 0];
3311
- }
3312
- text.nodeValue = chars.join("");
3313
- }
3314
- }, 260);
3315
- cleanups.push(() => {
3316
- for (const [text, value] of originals) {
3317
- if (text.isConnected && text.nodeValue !== value) text.nodeValue = value;
3318
- }
3319
- originals.clear();
3320
- });
3000
+ return loading;
3321
3001
  }
3322
- function spawnFallingLetters(layer) {
3323
- const sub = document.createElement("div");
3324
- sub.className = "rk-fx-layer";
3325
- layer.appendChild(sub);
3326
- const headings = [...document.querySelectorAll("h1, h2, h3")].filter((el) => {
3327
- const r = el.getBoundingClientRect();
3328
- return r.width > 0 && r.top < window.innerHeight && r.bottom > 0 && !el.closest("[data-review-kit-host]");
3329
- });
3330
- let spawned = 0;
3331
- for (const heading of headings.slice(0, 3)) {
3332
- const textNode = [...heading.childNodes].find((n) => n.nodeType === 3 && (n.nodeValue ?? "").trim().length > 3);
3333
- const source = textNode ?? heading.firstChild;
3334
- if (!source || source.nodeType !== 3) continue;
3335
- const value = source.nodeValue ?? "";
3336
- const range = document.createRange();
3337
- for (let i = 0; i < value.length && spawned < 26; i++) {
3338
- if (value[i] === " " || Math.random() < 0.55) continue;
3339
- range.setStart(source, i);
3340
- range.setEnd(source, i + 1);
3341
- const rect = range.getBoundingClientRect();
3342
- if (rect.width === 0) continue;
3343
- const span = document.createElement("span");
3344
- span.className = "rk-fx-letter";
3345
- span.textContent = value[i];
3346
- span.style.left = `${rect.left}px`;
3347
- span.style.top = `${rect.top}px`;
3348
- span.style.fontSize = `${rect.height}px`;
3349
- span.style.animationDelay = `${Math.random() * 900 | 0}ms`;
3350
- span.style.setProperty("--fx-drift", `${(Math.random() * 120 | 0) - 60}px`);
3351
- sub.appendChild(span);
3352
- spawned++;
3353
- }
3354
- }
3355
- window.setTimeout(() => sub.remove(), 4600);
3356
- }
3357
- function spawnGlitchBands(layer, cleanups) {
3358
- for (let i = 0; i < 3; i++) {
3359
- const band = document.createElement("div");
3360
- band.className = "rk-fx-band";
3361
- band.style.height = `${34 + i * 16}px`;
3362
- band.style.animationDuration = `${2.8 + i * 0.7}s`;
3363
- band.style.animationDelay = `${i * 0.9}s`;
3364
- layer.appendChild(band);
3365
- cleanups.push(() => band.remove());
3366
- }
3367
- }
3368
- var startLava = bigEffect(({ layer, reduced, addStyle, later, every, wave, decayText, cleanups }) => {
3369
- if (reduced) {
3370
- addStyle(`@keyframes rk-lava-soft { 0%,100% { filter: none; } 50% { filter: sepia(0.35) hue-rotate(-20deg) saturate(1.3); } }
3371
- ${CHAOS_SEL} { animation: rk-lava-soft 9s ease-in-out infinite; }`);
3372
- wave({ kind: "rain", glyphs: ["\u{1FAE0}", "\u{1F525}"], count: 8, durationMs: 9e3 });
3373
- every(() => wave({ kind: "rain", glyphs: ["\u{1FAE0}", "\u{1F4A7}"], count: 6, durationMs: 9e3 }), 14e3);
3374
- return;
3375
- }
3376
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3377
- svg.setAttribute("data-rk-fx", "");
3378
- svg.setAttribute("width", "0");
3379
- svg.setAttribute("height", "0");
3380
- svg.style.position = "absolute";
3381
- svg.innerHTML = `<defs><filter id="rk-lava-warp" x="-10%" y="-10%" width="120%" height="130%">
3382
- <feTurbulence type="fractalNoise" baseFrequency="0.008 0.003" numOctaves="2" result="n" seed="4">
3383
- <animate attributeName="baseFrequency" dur="7s" values="0.008 0.003;0.014 0.006;0.008 0.003" repeatCount="indefinite"/>
3384
- </feTurbulence>
3385
- <feDisplacementMap in="SourceGraphic" in2="n" scale="10" xChannelSelector="R" yChannelSelector="G">
3386
- <animate attributeName="scale" dur="6s" values="4;24;4" repeatCount="indefinite"/>
3387
- </feDisplacementMap>
3388
- </filter></defs>`;
3389
- document.body.appendChild(svg);
3390
- cleanups.push(() => svg.remove());
3391
- addStyle(`@keyframes rk-lava1 {
3392
- 0% { filter: none; transform: scale(1.22); }
3393
- 35% { filter: sepia(0.4) hue-rotate(-24deg) saturate(1.8) blur(0.8px); transform: scale(1.235) skewY(0.5deg) translateY(6px); }
3394
- 70% { filter: sepia(0.6) hue-rotate(-38deg) saturate(2.2) contrast(1.1) blur(1.6px); transform: scale(1.22) scaleY(1.03) translateY(14px) skewX(-0.6deg); }
3395
- 100% { filter: none; transform: scale(1.22); }
3396
- }
3397
- ${CHAOS_SEL} { animation: rk-lava1 7s ease-in-out infinite; }`);
3398
- later(() => {
3399
- addStyle(`@keyframes rk-lava2 {
3400
- 0% { filter: url(#rk-lava-warp) sepia(0.5) hue-rotate(-30deg) saturate(2); transform: scale(1.22) translateY(10px); }
3401
- 40% { filter: url(#rk-lava-warp) sepia(0.8) hue-rotate(-48deg) saturate(2.8) contrast(1.18) blur(2.4px); transform: scale(1.2) scaleY(1.1) translateY(46px) skewX(1deg); }
3402
- 70% { filter: url(#rk-lava-warp) sepia(0.7) hue-rotate(-40deg) saturate(2.4) blur(1.2px); transform: scale(1.23) scaleY(1.06) translateY(28px) skewY(-0.8deg); }
3403
- 100% { filter: url(#rk-lava-warp) sepia(0.5) hue-rotate(-30deg) saturate(2); transform: scale(1.22) translateY(10px); }
3404
- }
3405
- ${CHAOS_SEL} { animation: rk-lava2 6.5s ease-in-out infinite; }
3406
- @keyframes rk-lava-text { 0%,100% { letter-spacing: normal; } 50% { letter-spacing: 0.1em; word-spacing: 0.25em; text-shadow: 0 6px 6px rgba(255,80,0,0.5); } }
3407
- h1, h2, h3, p, a, li, button { animation: rk-lava-text 5s ease-in-out infinite; }`);
3408
- decayText();
3409
- }, 1e4);
3410
- const drips = document.createElement("div");
3411
- drips.className = "rk-fx-layer";
3412
- layer.appendChild(drips);
3413
- cleanups.push(() => drips.remove());
3414
- every(() => {
3415
- const d = document.createElement("div");
3416
- d.className = "rk-fx-drip";
3417
- d.style.left = `${(Math.random() * 96 + 2).toFixed(1)}vw`;
3418
- d.style.animationDuration = `${(3.5 + Math.random() * 3).toFixed(2)}s`;
3419
- d.style.background = `linear-gradient(180deg, transparent, hsl(${Math.random() * 30 + 8 | 0}, 95%, 55%))`;
3420
- drips.appendChild(d);
3421
- window.setTimeout(() => d.remove(), 7e3);
3422
- }, 700);
3423
- wave({ kind: "rain", glyphs: ["\u{1F525}", "\u{1FAE0}", "\u{1F321}\uFE0F", "\u{1F4A7}"], count: 30, durationMs: 1e4 });
3424
- every(() => wave({ kind: "rain", glyphs: ["\u{1F525}", "\u{1FAE0}", "\u{1F4A7}"], count: 22, durationMs: 9e3 }), 9500);
3425
- });
3426
- var startSklo = bigEffect(({ layer, reduced, addStyle, every, wave, decayText, cleanups }) => {
3427
- if (reduced) {
3428
- addStyle(`@keyframes rk-sklo-soft { 0%,100% { filter: none; } 50% { filter: saturate(0.7) brightness(1.06); } }
3429
- ${CHAOS_SEL} { animation: rk-sklo-soft 8s ease-in-out infinite; }`);
3430
- wave({ kind: "burst", glyphs: ["\u2727", "\u2756", "\u{1F4A0}"], count: 10, durationMs: 8e3 });
3431
- return;
3432
- }
3433
- addStyle(`@keyframes rk-sklo-base {
3434
- 0%,10% { transform: scale(1.12); filter: none; }
3435
- 16% { transform: scale(1.125) rotate(0.4deg); filter: brightness(1.12) contrast(1.06); }
3436
- 50% { transform: scale(1.1) rotate(-0.5deg); filter: saturate(0.65) brightness(1.05) blur(0.5px); }
3437
- 72% { transform: scale(1.12); filter: none; }
3438
- 100% { transform: scale(1.12); filter: none; }
3439
- }
3440
- ${CHAOS_SEL} { animation: rk-sklo-base 11s ease-in-out infinite; }
3441
- @keyframes rk-sklo-fly {
3442
- 0%, 12% { transform: translate(0, 0) rotate(0); opacity: 1; }
3443
- 30% { transform: translate(var(--rk-gx), var(--rk-gy)) rotate(var(--rk-gr)); opacity: 0.92; }
3444
- 52% { transform: translate(calc(var(--rk-gx) * 1.18), calc(var(--rk-gy) * 1.18)) rotate(calc(var(--rk-gr) * 1.25)); opacity: 0.85; }
3445
- 74% { transform: translate(0, 0) rotate(0); opacity: 1; }
3446
- 100% { transform: translate(0, 0) rotate(0); opacity: 1; }
3447
- }
3448
- [data-rk-shard] { animation: rk-sklo-fly 11s cubic-bezier(0.55, 0, 0.3, 1) infinite; }`);
3449
- const shards = [...document.querySelectorAll("h1, h2, h3, h4, p, li, img, button, a")].filter((el) => {
3450
- if (el.closest("[data-review-kit-host]")) return false;
3451
- const r = el.getBoundingClientRect();
3452
- return r.width > 8 && r.height > 8 && r.bottom > -200 && r.top < window.innerHeight + 200;
3453
- }).slice(0, 90);
3454
- const rules = [];
3455
- shards.forEach((el, i) => {
3456
- const angle = i * 137.5 * Math.PI / 180;
3457
- const dist = 18 + i * 29 % 46;
3458
- rules.push(
3459
- `[data-rk-shard="${i}"] { --rk-gx: ${(Math.cos(angle) * dist).toFixed(1)}vw; --rk-gy: ${(Math.sin(angle) * dist).toFixed(1)}vh; --rk-gr: ${i * 97 % 640 - 320}deg; }`
3460
- );
3461
- el.setAttribute("data-rk-shard", String(i));
3462
- });
3463
- addStyle(rules.join("\n"));
3464
- cleanups.push(() => {
3465
- for (const el of shards) el.removeAttribute("data-rk-shard");
3466
- });
3467
- const cracks = document.createElement("div");
3468
- cracks.className = "rk-fx-cracks";
3469
- cracks.innerHTML = Array.from({ length: 9 }, (_, i) => {
3470
- const angle = i * 40 + i % 3 * 7;
3471
- return `<span style="transform: rotate(${angle}deg); animation-delay: ${(i * 0.05).toFixed(2)}s"></span>`;
3472
- }).join("");
3473
- layer.appendChild(cracks);
3474
- cleanups.push(() => cracks.remove());
3475
- decayText();
3476
- every(() => wave({ kind: "burst", glyphs: ["\u2727", "\u2756", "\u{1F4A0}", "\u{1F9CA}"], count: 26, durationMs: 5e3 }), 11e3);
3477
- wave({ kind: "burst", glyphs: ["\u2727", "\u2756", "\u{1F4A0}"], count: 26, durationMs: 5e3 });
3478
- });
3479
- var startVir = bigEffect(({ layer, reduced, addStyle, later, wave, decayText, cleanups }) => {
3480
- if (reduced) {
3481
- addStyle(`@keyframes rk-vir-soft { 0%,100% { filter: none; } 50% { filter: hue-rotate(50deg); } }
3482
- ${CHAOS_SEL} { animation: rk-vir-soft 9s ease-in-out infinite; }`);
3483
- wave({ kind: "burst", glyphs: ["\u{1F300}"], count: 8, durationMs: 8e3 });
3484
- return;
3485
- }
3486
- addStyle(`@keyframes rk-vir1 {
3487
- 0% { transform: scale(1.25) rotate(0deg); filter: none; }
3488
- 30% { transform: scale(1.31) rotate(4deg); filter: hue-rotate(50deg) saturate(1.3) blur(0.5px); }
3489
- 60% { transform: scale(1.28) rotate(-3.4deg); filter: hue-rotate(140deg) saturate(0.8) blur(1px); }
3490
- 100% { transform: scale(1.25) rotate(0deg); filter: none; }
3491
- }
3492
- ${CHAOS_SEL} { animation: rk-vir1 6.5s ease-in-out infinite; }`);
3493
- later(() => {
3494
- addStyle(`@keyframes rk-vir2 {
3495
- 0% { transform: scale(1.25) rotate(0deg); filter: hue-rotate(0deg) saturate(1.4); }
3496
- 25% { transform: scale(1.42) rotate(9deg); filter: hue-rotate(90deg) saturate(1.9) blur(1.6px); }
3497
- 50% { transform: scale(1.55) rotate(-7deg); filter: hue-rotate(200deg) saturate(0.6) contrast(1.15) blur(2.6px); }
3498
- 75% { transform: scale(1.38) rotate(10deg); filter: hue-rotate(300deg) saturate(1.8) blur(1.2px); }
3499
- 100% { transform: scale(1.25) rotate(0deg); filter: hue-rotate(360deg) saturate(1.4); }
3500
- }
3501
- ${CHAOS_SEL} { animation: rk-vir2 5.5s ease-in-out infinite; }
3502
- @keyframes rk-vir-text { 0%,100% { letter-spacing: normal; } 50% { letter-spacing: 0.16em; text-shadow: 3px 0 rgba(0,180,255,0.5), -3px 0 rgba(255,60,160,0.5); } }
3503
- h1, h2, h3, p, a, li, button { animation: rk-vir-text 4.5s ease-in-out infinite; }`);
3504
- decayText();
3505
- }, 12e3);
3506
- const GLYPHS = ["\u{1F300}", "\u2728", "\u{1F343}", "\u{1F4C4}", "\u2604\uFE0F"];
3507
- const bits = [];
3508
- const cx = () => window.innerWidth / 2;
3509
- const cy = () => window.innerHeight / 2;
3510
- const maxR = () => Math.hypot(window.innerWidth, window.innerHeight) / 2 + 60;
3511
- for (let i = 0; i < 36; i++) {
3512
- const el = document.createElement("span");
3513
- el.className = "rk-fx-particle";
3514
- el.textContent = GLYPHS[i % GLYPHS.length];
3515
- el.style.fontSize = `${13 + i * 11 % 16}px`;
3516
- layer.appendChild(el);
3517
- bits.push({ el, r: maxR() * ((i + 1) / 36), a: i * 0.7, v: 0.6 + i % 5 * 0.16 });
3518
- }
3519
- let raf = 0;
3520
- const tick = () => {
3521
- raf = requestAnimationFrame(tick);
3522
- for (const bit of bits) {
3523
- bit.a += 0.014 * bit.v * (1 + 140 / Math.max(bit.r, 40));
3524
- bit.r -= 0.55 * bit.v;
3525
- if (bit.r < 16) bit.r = maxR();
3526
- bit.el.style.transform = `translate3d(${cx() + Math.cos(bit.a) * bit.r}px, ${cy() + Math.sin(bit.a) * bit.r}px, 0) rotate(${bit.a}rad)`;
3527
- bit.el.style.opacity = `${Math.min(1, bit.r / 120)}`;
3528
- }
3529
- };
3530
- raf = requestAnimationFrame(tick);
3531
- cleanups.push(() => {
3532
- cancelAnimationFrame(raf);
3533
- bits.forEach((bit) => bit.el.remove());
3534
- });
3535
- });
3536
- var startCrt = bigEffect(({ layer, reduced, addStyle, every, wave, decayText, cleanups }) => {
3537
- if (reduced) {
3538
- addStyle(`${CHAOS_SEL} { filter: sepia(0.7) hue-rotate(55deg) saturate(1.6) contrast(1.05); transition: filter 2s ease; }`);
3539
- return;
3002
+ function cancelWhenReady() {
3003
+ if (loaded) return loaded.cancelEffects();
3004
+ if (loading && queued > 0) {
3005
+ loading.then((mod) => mod.cancelEffects()).catch(() => void 0);
3006
+ return true;
3540
3007
  }
3541
- const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
3542
- svg.setAttribute("data-rk-fx", "");
3543
- svg.setAttribute("width", "0");
3544
- svg.setAttribute("height", "0");
3545
- svg.style.position = "absolute";
3546
- svg.innerHTML = `<defs><filter id="rk-crt-tear" x="-10%" y="-10%" width="120%" height="120%">
3547
- <feTurbulence type="turbulence" baseFrequency="0.002 0.09" numOctaves="1" result="n" seed="11">
3548
- <animate attributeName="seed" dur="3s" values="11;37;11" repeatCount="indefinite"/>
3549
- </feTurbulence>
3550
- <feDisplacementMap in="SourceGraphic" in2="n" scale="0" xChannelSelector="R" yChannelSelector="G">
3551
- <animate attributeName="scale" dur="8s" values="0;0;26;4;0;0" keyTimes="0;0.55;0.62;0.7;0.78;1" repeatCount="indefinite"/>
3552
- </feDisplacementMap>
3553
- </filter></defs>`;
3554
- document.body.appendChild(svg);
3555
- cleanups.push(() => svg.remove());
3556
- addStyle(`@keyframes rk-crt-hold {
3557
- 0%, 60% { transform: scale(1.16) translate(0, 0); }
3558
- 64% { transform: scale(1.16) translate(-3px, 14px) skewX(-0.8deg); }
3559
- 68% { transform: scale(1.16) translate(2px, -8px); }
3560
- 74%, 100% { transform: scale(1.16) translate(0, 0); }
3561
- }
3562
- ${CHAOS_SEL} {
3563
- filter: url(#rk-crt-tear) sepia(1) hue-rotate(55deg) saturate(2.6) contrast(1.22) brightness(0.92);
3564
- animation: rk-crt-hold 8s linear infinite;
3565
- }
3566
- @keyframes rk-crt-text { 0%,100% { text-shadow: 1px 0 rgba(0,255,120,0.5); } 50% { text-shadow: -2px 0 rgba(0,255,120,0.7), 2px 0 rgba(0,120,60,0.5); letter-spacing: 0.06em; } }
3567
- h1, h2, h3, p, a, li, button { animation: rk-crt-text 5s ease-in-out infinite; }`);
3568
- const overlay = document.createElement("div");
3569
- overlay.className = "rk-fx-crt";
3570
- overlay.innerHTML = '<div class="rk-fx-crt-scan"></div><div class="rk-fx-crt-roll"></div><div class="rk-fx-crt-vignette"></div><div class="rk-fx-crt-osd">KAN\xC1L 69 \xB7 SLAB\xDD SIGN\xC1L</div>';
3571
- layer.appendChild(overlay);
3572
- cleanups.push(() => overlay.remove());
3573
- decayText();
3574
- every(() => wave({ kind: "rain", glyphs: ["\u{1F4FA}", "\u{1F4E1}", "\u{1D34D}"], count: 10, durationMs: 7e3 }), 16e3);
3575
- });
3576
- function startBanner({ layer, reduced }) {
3577
- const banner = document.createElement("div");
3578
- banner.className = "rk-fx-banner";
3579
- banner.textContent = "\u{1F6A8} KOKOT ALERT \u{1F6A8} KOKOT ALERT \u{1F6A8} KOKOT ALERT \u{1F6A8}";
3580
- if (reduced) banner.classList.add("rk-fx-banner-still");
3581
- layer.appendChild(banner);
3582
- return () => banner.remove();
3583
- }
3584
- function startVrtule({ reduced }) {
3585
- const style = document.createElement("style");
3586
- style.setAttribute("data-rk-fx", "vrtule");
3587
- style.textContent = reduced ? `@keyframes rk-vrtula { 0%,100% { transform: rotate(0); } 50% { transform: rotate(3deg); } }
3588
- img { animation: rk-vrtula 4s ease-in-out infinite; }` : `@keyframes rk-vrtula { from { transform: rotate(0) scale(1); } 50% { transform: rotate(180deg) scale(0.9); } to { transform: rotate(360deg) scale(1); } }
3589
- img { animation: rk-vrtula 3.6s ease-in-out infinite; }`;
3590
- document.head.appendChild(style);
3591
- return () => style.remove();
3592
- }
3593
- function makeChase(options) {
3594
- return function startChase({ layer, reduced }) {
3595
- const critters = [];
3596
- const count = reduced ? Math.min(4, options.count) : options.count;
3597
- for (let i = 0; i < count; i++) {
3598
- const el = document.createElement("span");
3599
- el.className = "rk-fx-particle";
3600
- el.textContent = options.glyphs[i % options.glyphs.length];
3601
- el.style.fontSize = `${options.sizePx(i)}px`;
3602
- layer.appendChild(el);
3603
- critters.push({ el, x: window.innerWidth / 2, y: window.innerHeight / 2, seed: i * 137 });
3604
- }
3605
- const wobble = options.wobbleT ?? 400;
3606
- let raf = 0;
3607
- const tick = (t) => {
3608
- raf = requestAnimationFrame(tick);
3609
- const root = layer.getRootNode();
3610
- const chips = [...root.querySelectorAll(".rk-peer-cursor")].filter((c) => c.style.opacity !== "0").map((c) => {
3611
- const m = /translate3d\(([-\d.]+)px, ([-\d.]+)px/.exec(c.style.transform);
3612
- return m ? { x: parseFloat(m[1]), y: parseFloat(m[2]) } : null;
3613
- }).filter((p) => p !== null);
3614
- chips.push({ x: lastPointerFallback.x, y: lastPointerFallback.y });
3615
- for (const critter of critters) {
3616
- const target = chips[critter.seed % chips.length];
3617
- const orbit = reduced ? 14 : options.orbit(critter.seed);
3618
- const wx = target.x + Math.cos(t / wobble + critter.seed) * orbit;
3619
- const wy = target.y + Math.sin(t / (wobble * 0.82) + critter.seed) * orbit;
3620
- const prevX = critter.x;
3621
- critter.x += (wx - critter.x) * options.lag;
3622
- critter.y += (wy - critter.y) * options.lag;
3623
- const face = options.flip && critter.x < prevX ? " scaleX(-1)" : "";
3624
- critter.el.style.transform = `translate3d(${critter.x}px, ${critter.y}px, 0)${face}`;
3625
- }
3626
- };
3627
- raf = requestAnimationFrame(tick);
3628
- const onMove = (event) => {
3629
- lastPointerFallback.x = event.clientX;
3630
- lastPointerFallback.y = event.clientY;
3631
- };
3632
- document.addEventListener("pointermove", onMove, { passive: true });
3633
- return () => {
3634
- cancelAnimationFrame(raf);
3635
- document.removeEventListener("pointermove", onMove);
3636
- for (const critter of critters) critter.el.remove();
3637
- };
3638
- };
3008
+ return false;
3639
3009
  }
3640
- var lastPointerFallback = { x: 300, y: 300 };
3641
- function styleEffect(css) {
3642
- const style = document.createElement("style");
3643
- style.setAttribute("data-rk-fx", "");
3644
- style.textContent = css;
3645
- document.head.appendChild(style);
3646
- return () => style.remove();
3647
- }
3648
- var PAGE_SEL = "body > :not([data-review-kit-host]):not(script):not(style)";
3649
- function startDisko({ layer, reduced }) {
3650
- const remove = styleEffect(
3651
- reduced ? `@keyframes rk-disko { 0%,100% { filter: none; } 50% { filter: hue-rotate(60deg) saturate(1.2); } }
3652
- ${PAGE_SEL} { animation: rk-disko 8s ease-in-out infinite; }` : `@keyframes rk-disko { from { filter: hue-rotate(0deg) saturate(1.35); } to { filter: hue-rotate(360deg) saturate(1.35); } }
3653
- ${PAGE_SEL} { animation: rk-disko 4s linear infinite; }`
3654
- );
3655
- spawnParticles(layer, { kind: "rain", glyphs: ["\u2728", "\u{1FAA9}", "\u{1F483}", "\u{1F57A}"], count: 26}, reduced, 15e3);
3656
- return remove;
3010
+ function setEffectBroadcaster(fn) {
3011
+ broadcaster = fn;
3012
+ loaded?.setEffectBroadcaster(fn);
3657
3013
  }
3658
- function startZrkadlo({ reduced }) {
3659
- if (reduced) {
3660
- return styleEffect(`${PAGE_SEL} { filter: saturate(0.8); transition: filter 2s ease; }`);
3661
- }
3662
- return styleEffect(`@keyframes rk-zrkadlo {
3663
- 0%, 100% { transform: perspective(1400px) rotateY(0); }
3664
- 35%, 65% { transform: perspective(1400px) rotateY(180deg); }
3665
- }
3666
- ${PAGE_SEL} { animation: rk-zrkadlo 12s ease-in-out both; transform-origin: 50% 50%; }`);
3667
- }
3668
- function startPohreb({ layer, reduced }) {
3669
- const remove = styleEffect(`@keyframes rk-pohreb { from { filter: none; } to { filter: grayscale(1) brightness(0.82) sepia(0.15); } }
3670
- ${PAGE_SEL} { animation: rk-pohreb 5s ease-out both; }`);
3671
- spawnParticles(layer, { kind: "rain", glyphs: ["\u{1F940}", "\u26B0\uFE0F", "\u{1F56F}\uFE0F", "\u{1F5A4}"], count: reduced ? 8 : 22}, reduced, 14e3);
3672
- return remove;
3673
- }
3674
- function startLaser({ layer, reduced }) {
3675
- const beams = [];
3676
- const count = reduced ? 2 : 5;
3677
- const hues = [330, 190, 120, 45, 265];
3678
- for (let i = 0; i < count; i++) {
3679
- const beam = document.createElement("div");
3680
- beam.className = "rk-fx-laser";
3681
- beam.style.background = `linear-gradient(90deg, transparent, hsla(${hues[i % hues.length]}, 100%, 60%, 0.55), transparent)`;
3682
- beam.style.left = `${i % 2 * 100}%`;
3683
- beam.style.animationDuration = `${3.4 + i * 0.6}s`;
3684
- beam.style.animationDelay = `${i * 0.45}s`;
3685
- if (i % 2) beam.style.animationDirection = "reverse";
3686
- layer.appendChild(beam);
3687
- beams.push(beam);
3688
- }
3689
- return () => beams.forEach((b) => b.remove());
3690
- }
3691
- function startKura({ layer, reduced }) {
3692
- const hen = document.createElement("span");
3693
- hen.className = "rk-fx-kura";
3694
- hen.textContent = "\u{1F414}";
3695
- layer.appendChild(hen);
3696
- const props = [hen];
3697
- const timers = [];
3698
- const walkMs = reduced ? 16e3 : 11e3;
3699
- hen.style.animationDuration = `${walkMs}ms`;
3700
- if (!reduced) {
3701
- for (let i = 1; i <= 5; i++) {
3702
- timers.push(
3703
- window.setTimeout(() => {
3704
- const egg = document.createElement("span");
3705
- egg.className = "rk-fx-egg";
3706
- egg.textContent = "\u{1F95A}";
3707
- egg.style.left = `${i / 6 * 100}%`;
3708
- layer.appendChild(egg);
3709
- props.push(egg);
3710
- timers.push(
3711
- window.setTimeout(() => {
3712
- egg.textContent = "\u{1F423}";
3713
- }, 2600 + i * 400)
3714
- );
3715
- }, walkMs * i / 6)
3716
- );
3717
- }
3718
- }
3719
- return () => {
3720
- timers.forEach((t) => window.clearTimeout(t));
3721
- props.forEach((p) => p.remove());
3722
- };
3014
+ function setEffectAssetsUrl(url) {
3015
+ assetsUrl = url ?? null;
3016
+ loaded?.setFxAssetsUrl(assetsUrl);
3723
3017
  }
3724
- function startSpam({ layer, reduced }) {
3725
- const MESSAGES = [
3726
- "\u26A0\uFE0F Kritick\xE1 chyba: str\xE1nka je pr\xEDli\u0161 pekn\xE1",
3727
- "\u{1F525} 69 \u013Eud\xED pr\xE1ve pozer\xE1 tento button",
3728
- "\u{1F4BE} Uklad\xE1m tvoje hriechy\u2026",
3729
- "\u{1F4C8} Konverzia st\xFApla o 420 %",
3730
- "\u{1F9E0} AI navrhuje: prerob to cel\xE9",
3731
- "\u{1F36A} T\xE1to str\xE1nka \u017Eerie kol\xE1\u010Diky",
3732
- "\u{1F4DE} Vol\xE1 ti klient. Zdvihni.",
3733
- "\u{1F4B8} Fakt\xFAra po splatnosti (odjak\u017Eiva)",
3734
- "\u{1F6F8} Zisten\xE1 mimozemsk\xE1 aktivita v p\xE4ti\u010Dke",
3735
- "\u2705 Ni\u010D sa nepodarilo \xFAspe\u0161ne"
3736
- ];
3737
- const toasts = [];
3738
- const timers = [];
3739
- const total = reduced ? 4 : MESSAGES.length;
3740
- for (let i = 0; i < total; i++) {
3741
- timers.push(
3742
- window.setTimeout(() => {
3743
- const toast2 = document.createElement("div");
3744
- toast2.className = "rk-fx-toast";
3745
- toast2.textContent = MESSAGES[i % MESSAGES.length];
3746
- toast2.style.top = `${14 + i % 5 * 58}px`;
3747
- layer.appendChild(toast2);
3748
- toasts.push(toast2);
3749
- timers.push(window.setTimeout(() => toast2.remove(), 5200));
3750
- }, i * (reduced ? 1800 : 950))
3751
- );
3752
- }
3753
- return () => {
3754
- timers.forEach((t) => window.clearTimeout(t));
3755
- toasts.forEach((t) => t.remove());
3756
- };
3018
+ function cancelEffects() {
3019
+ return cancelWhenReady();
3757
3020
  }
3758
- var EFFECTS = {
3759
- ondro: { kind: "custom", durationMs: ONDRO_MS, start: startOndro },
3760
- lava: { kind: "custom", durationMs: BIG_MS, start: startLava },
3761
- sklo: { kind: "custom", durationMs: BIG_MS, start: startSklo },
3762
- vir: { kind: "custom", durationMs: BIG_MS, start: startVir },
3763
- crt: { kind: "custom", durationMs: BIG_MS, start: startCrt },
3764
- hovno: { kind: "rain", glyphs: ["\u{1F4A9}"], count: 44, durationMs: 4200 },
3765
- konfety: { kind: "burst", glyphs: ["\u{1F389}", "\u{1F38A}", "\u2728"], count: 56, durationMs: 3600 },
3766
- srdiecka: { kind: "rain", glyphs: ["\u2764\uFE0F", "\u{1F49A}", "\u{1F49B}"], count: 40, durationMs: 4200 },
3767
- sneh: { kind: "rain", glyphs: ["\u2744\uFE0F", "\u{1F328}\uFE0F"], count: 48, durationMs: 5200 },
3768
- zemetrasenie: { kind: "rain", glyphs: ["\u{1FAA8}", "\u{1F9F1}"], count: 18, durationMs: 2400, shake: true },
3769
- "69": { kind: "burst", glyphs: ["\u{1F346}", "\u{1F351}", "\u{1F4A6}"], count: 69, durationMs: NICE_MS },
3770
- kokot: { kind: "custom", durationMs: 14500, start: startBanner },
3771
- vrtule: { kind: "custom", durationMs: NICE_MS, start: startVrtule },
3772
- muchy: {
3773
- kind: "custom",
3774
- durationMs: NICE_MS,
3775
- start: makeChase({ glyphs: ["\u{1FAB0}"], count: 14, sizePx: (i) => 12 + i % 3 * 4, orbit: (s) => 26 + s % 20, lag: 0.12, wobbleT: 400 })
3776
- },
3777
- pes: {
3778
- kind: "custom",
3779
- durationMs: 12e3,
3780
- start: makeChase({ glyphs: ["\u{1F415}", "\u{1F429}", "\u{1F9AE}"], count: 3, sizePx: (i) => 26 + i % 2 * 6, orbit: (s) => 46 + s % 30, lag: 0.055, wobbleT: 900, flip: true })
3781
- },
3782
- pivo: { kind: "rain", glyphs: ["\u{1F37A}", "\u{1F37B}", "\u{1F37E}"], count: 42, durationMs: 8e3 },
3783
- nikotin: { kind: "rain", glyphs: ["\u{1F6AC}", "\u{1F4A8}", "\u{1F62E}\u200D\u{1F4A8}"], count: 20, durationMs: 13e3 },
3784
- matrix: { kind: "rain", glyphs: [..."\u30A2\u30AB\u30B5\u30BF\u30CA0169\uFF8A\uFF8F\uFF94\uFF97\uFF66"], count: 64, durationMs: 12e3, className: "rk-fx-matrix" },
3785
- disko: { kind: "custom", durationMs: 15e3, start: startDisko },
3786
- zrkadlo: { kind: "custom", durationMs: 12e3, start: startZrkadlo },
3787
- pohreb: { kind: "custom", durationMs: 14e3, start: startPohreb },
3788
- laser: { kind: "custom", durationMs: 12e3, start: startLaser },
3789
- kura: { kind: "custom", durationMs: 11500, start: startKura },
3790
- spam: { kind: "custom", durationMs: 11e3, start: startSpam }
3791
- };
3792
3021
  function runEffect(name, layerHost) {
3022
+ if (!isEffectCommand(name)) return false;
3793
3023
  if (name === "stop") {
3794
- cancelEffects();
3024
+ cancelWhenReady();
3795
3025
  return true;
3796
3026
  }
3797
- const spec = EFFECTS[name];
3798
- if (!spec || !layerHost) return spec !== void 0;
3799
- const reduced = reducedMotion();
3800
- const duration = reduced && spec.kind !== "custom" ? Math.min(2e3, spec.durationMs) : spec.durationMs;
3801
- const layer = document.createElement("div");
3802
- layer.className = "rk-fx-layer";
3803
- layerHost.appendChild(layer);
3804
- let customCleanup = null;
3805
- let shakeCleanup = null;
3806
- if (spec.kind === "custom") {
3807
- customCleanup = spec.start({ layer, reduced });
3808
- } else {
3809
- spawnParticles(layer, spec, reduced, duration);
3810
- if (spec.shake && !reduced) shakeCleanup = runShake();
3811
- }
3812
- let timer = 0;
3813
- const cancel = () => {
3814
- window.clearTimeout(timer);
3815
- customCleanup?.();
3816
- shakeCleanup?.();
3817
- layer.remove();
3818
- active.delete(cancel);
3819
- };
3820
- timer = window.setTimeout(cancel, duration + 1200);
3821
- active.add(cancel);
3027
+ queued += 1;
3028
+ void load().then((mod) => {
3029
+ queued = Math.max(0, queued - 1);
3030
+ mod.runEffect(name, layerHost);
3031
+ }).catch(() => {
3032
+ });
3822
3033
  return true;
3823
3034
  }
3824
3035
 
@@ -4118,21 +3329,24 @@ var SHORTCUTS = [
4118
3329
  }
4119
3330
  }
4120
3331
  ];
3332
+
3333
+ // src/client/emblemaPush.ts
3334
+ import { useEffect as useEffect2, useRef as useRef2 } from "react";
4121
3335
  var AUTO_PUSH_DEBOUNCE_MS = 500;
4122
3336
  function useEmblemaAutoPush(state, dispatch, config) {
4123
3337
  const emblema = config.emblema;
4124
- const sessionRef = useRef(state.session);
3338
+ const sessionRef = useRef2(state.session);
4125
3339
  sessionRef.current = state.session;
4126
- const lastSent = useRef(/* @__PURE__ */ new Map());
4127
- const inFlight = useRef(false);
4128
- const alive = useRef(true);
4129
- useEffect(() => {
3340
+ const lastSent = useRef2(/* @__PURE__ */ new Map());
3341
+ const inFlight = useRef2(false);
3342
+ const alive = useRef2(true);
3343
+ useEffect2(() => {
4130
3344
  alive.current = true;
4131
3345
  return () => {
4132
3346
  alive.current = false;
4133
3347
  };
4134
3348
  }, []);
4135
- const flushRef = useRef(() => {
3349
+ const flushRef = useRef2(() => {
4136
3350
  });
4137
3351
  flushRef.current = () => {
4138
3352
  if (!alive.current || !emblema) return;
@@ -4154,12 +3368,12 @@ function useEmblemaAutoPush(state, dispatch, config) {
4154
3368
  for (const item of changed) lastSent.current.set(item.id, item);
4155
3369
  if (alive.current) dispatch({ type: "EMBLEMA_PUSH_STATUS", status: "ok" });
4156
3370
  } else if (alive.current) {
4157
- dispatch({ type: "EMBLEMA_PUSH_STATUS", status: "offline" });
3371
+ dispatch({ type: "EMBLEMA_PUSH_STATUS", status: result.reason === "rejected" ? "rejected" : "offline" });
4158
3372
  }
4159
3373
  });
4160
3374
  };
4161
3375
  const items = state.session?.items;
4162
- useEffect(() => {
3376
+ useEffect2(() => {
4163
3377
  if (!emblema || !items) return;
4164
3378
  for (const id of [...lastSent.current.keys()]) {
4165
3379
  if (!items.some((item) => item.id === id)) lastSent.current.delete(id);
@@ -4173,6 +3387,9 @@ function useEmblemaAutoPush(state, dispatch, config) {
4173
3387
  }, [items, emblema]);
4174
3388
  }
4175
3389
 
3390
+ // src/client/sync.ts
3391
+ import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3 } from "react";
3392
+
4176
3393
  // realtime/client/rkSync.ts
4177
3394
  var RK_SYNC_PROTOCOL_VERSION = 1;
4178
3395
  var BACKOFF_BASE_MS = 1e3;
@@ -4303,8 +3520,8 @@ function createRkSync(options) {
4303
3520
  log(`welcome: ${items.length} items, ${peers.length} peers, ${deleted.length} tombstones, flushing ${queue.length}`);
4304
3521
  safely(() => handlers.onSnapshot?.(items, peers, deleted));
4305
3522
  while (queue.length > 0) {
4306
- const queued = queue[0];
4307
- if (!rawSend(queued)) break;
3523
+ const queued2 = queue[0];
3524
+ if (!rawSend(queued2)) break;
4308
3525
  queue.shift();
4309
3526
  }
4310
3527
  break;
@@ -4356,8 +3573,8 @@ function createRkSync(options) {
4356
3573
  if (typeof msg.peerId === "string") {
4357
3574
  const peerId = msg.peerId;
4358
3575
  const itemId = typeof msg.itemId === "string" ? msg.itemId : null;
4359
- const active2 = msg.active === true;
4360
- safely(() => handlers.onPeerTyping?.(peerId, itemId, active2));
3576
+ const active = msg.active === true;
3577
+ safely(() => handlers.onPeerTyping?.(peerId, itemId, active));
4361
3578
  }
4362
3579
  break;
4363
3580
  case "item:add": {
@@ -4388,6 +3605,8 @@ function createRkSync(options) {
4388
3605
  safely(() => handlers.onItemDelete?.(id, authorId));
4389
3606
  }
4390
3607
  break;
3608
+ default:
3609
+ break;
4391
3610
  }
4392
3611
  };
4393
3612
  const connect = () => {
@@ -4471,8 +3690,8 @@ function createRkSync(options) {
4471
3690
  sendSelecting(selector) {
4472
3691
  sendEphemeral({ t: "selecting", selector });
4473
3692
  },
4474
- sendTyping(itemId, active2) {
4475
- sendEphemeral({ t: "typing", itemId, active: active2 });
3693
+ sendTyping(itemId, active) {
3694
+ sendEphemeral({ t: "typing", itemId, active });
4476
3695
  },
4477
3696
  sendEffect(name) {
4478
3697
  sendEphemeral({ t: "fx", name });
@@ -4517,7 +3736,6 @@ function currentAuthor(name) {
4517
3736
  }
4518
3737
 
4519
3738
  // src/client/sync.ts
4520
- var DEFAULT_SYNC_URL = "wss://agropolio.fucking.style/rk-sync";
4521
3739
  function toPeerState(peer) {
4522
3740
  return {
4523
3741
  id: peer.id,
@@ -4541,21 +3759,21 @@ function sanitizeRemoteItem(raw) {
4541
3759
  return { ...raw, target };
4542
3760
  }
4543
3761
  function useRealtimeSync(state, rawDispatch, config, reducer2) {
4544
- const syncRef = useRef(null);
4545
- const stateRef = useRef(state);
3762
+ const syncRef = useRef3(null);
3763
+ const stateRef = useRef3(state);
4546
3764
  stateRef.current = state;
4547
- const myIdRef = useRef("");
4548
- const enabled = typeof config.syncToken === "string" && config.syncToken !== "";
3765
+ const myIdRef = useRef3("");
3766
+ const enabled = typeof config.syncToken === "string" && config.syncToken !== "" && typeof config.syncUrl === "string" && config.syncUrl !== "";
4549
3767
  const author = state.session?.author ?? "";
4550
3768
  const hasSession = state.session !== null;
4551
- useEffect(() => {
3769
+ useEffect3(() => {
4552
3770
  if (!enabled || !hasSession) return;
4553
3771
  const id = reviewerId();
4554
3772
  myIdRef.current = id;
4555
3773
  const dispatch = rawDispatch;
4556
3774
  const sync = createRkSync({
4557
3775
  token: config.syncToken,
4558
- url: config.syncUrl ?? DEFAULT_SYNC_URL,
3776
+ url: config.syncUrl,
4559
3777
  user: { id, name: author || "Reviewer", color: colorForReviewer(id) },
4560
3778
  handlers: {
4561
3779
  onStatus: (status) => dispatch({ type: "SYNC_STATUS", status }),
@@ -4580,7 +3798,7 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
4580
3798
  runEffect(name, layerRoot);
4581
3799
  },
4582
3800
  onPeerSelecting: (peerId, selector) => dispatch({ type: "PEER_SELECTING", peerId, selector }),
4583
- onPeerTyping: (peerId, itemId, active2) => dispatch({ type: "PEER_TYPING", peerId, itemId, active: active2 }),
3801
+ onPeerTyping: (peerId, itemId, active) => dispatch({ type: "PEER_TYPING", peerId, itemId, active }),
4584
3802
  onItemAdd: (item, authorId) => {
4585
3803
  const safeItem = sanitizeRemoteItem(item);
4586
3804
  if (!safeItem) return;
@@ -4619,7 +3837,7 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
4619
3837
  dispatch({ type: "PEERS_SET", peers: [] });
4620
3838
  };
4621
3839
  }, [enabled, hasSession, config.syncToken, config.syncUrl, author, rawDispatch]);
4622
- const wrapped = useMemo(() => {
3840
+ const wrapped = useMemo2(() => {
4623
3841
  return (action) => {
4624
3842
  const prev = stateRef.current;
4625
3843
  rawDispatch(action);
@@ -4641,7 +3859,7 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
4641
3859
  }
4642
3860
  };
4643
3861
  }, [rawDispatch, reducer2]);
4644
- useEffect(() => {
3862
+ useEffect3(() => {
4645
3863
  if (!enabled) return;
4646
3864
  let lastEl = null;
4647
3865
  let lastSel = null;
@@ -4670,18 +3888,21 @@ function useRealtimeSync(state, rawDispatch, config, reducer2) {
4670
3888
  return () => document.removeEventListener("pointermove", onPointerMove);
4671
3889
  }, [enabled]);
4672
3890
  const selectingSelector = state.editor?.target.selector ?? state.selection?.target.selector ?? null;
4673
- useEffect(() => {
3891
+ useEffect3(() => {
4674
3892
  if (!enabled) return;
4675
3893
  syncRef.current?.sendSelecting(selectingSelector);
4676
3894
  }, [enabled, selectingSelector]);
4677
3895
  const editorOpen = state.editor !== null;
4678
- useEffect(() => {
3896
+ useEffect3(() => {
4679
3897
  if (!enabled) return;
4680
3898
  syncRef.current?.sendTyping(null, editorOpen);
4681
3899
  }, [enabled, editorOpen]);
4682
3900
  return enabled ? wrapped : rawDispatch;
4683
3901
  }
4684
3902
 
3903
+ // src/client/components/Badge.tsx
3904
+ import { useLayoutEffect, useRef as useRef4, useState } from "react";
3905
+
4685
3906
  // src/client/dragPosition.ts
4686
3907
  var MARGIN = 8;
4687
3908
  var SNAP = 14;
@@ -4723,6 +3944,9 @@ function snapClampPos(x, y, width, height) {
4723
3944
  if (y > vh - height - 16 - SNAP) y = vh - height - 16;
4724
3945
  return clampPos(x, y, width, height);
4725
3946
  }
3947
+
3948
+ // src/client/components/Badge.tsx
3949
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
4726
3950
  var POS_KEY = "review-kit:badge-pos";
4727
3951
  var DRAG_THRESHOLD = 6;
4728
3952
  function defaultPos(corner, width, height) {
@@ -4745,16 +3969,16 @@ function Badge() {
4745
3969
  const { session, mode, panelOpen } = useStore();
4746
3970
  const dispatch = useDispatch();
4747
3971
  const config = useConfig();
4748
- const ref = useRef(null);
4749
- const clickTimer = useRef(null);
4750
- const lastClickAt = useRef(0);
3972
+ const ref = useRef4(null);
3973
+ const clickTimer = useRef4(null);
3974
+ const lastClickAt = useRef4(0);
4751
3975
  const [pos, setPos] = useState(() => readStoredPos(POS_KEY));
4752
3976
  const [dragging, setDragging] = useState(false);
4753
- const drag = useRef(null);
3977
+ const drag = useRef4(null);
4754
3978
  const openCount = session?.items.filter((item) => item.status === "open").length ?? 0;
4755
3979
  const applyPos = (next, persist) => {
4756
3980
  setPos(next);
4757
- storePos(POS_KEY, next);
3981
+ if (persist) storePos(POS_KEY, next);
4758
3982
  const el = ref.current;
4759
3983
  if (el) {
4760
3984
  const effective = next ?? defaultPos(config.position ?? "bottom-right", el.offsetWidth, el.offsetHeight);
@@ -4828,7 +4052,7 @@ function Badge() {
4828
4052
  if (clickTimer.current !== null) window.clearTimeout(clickTimer.current);
4829
4053
  clickTimer.current = null;
4830
4054
  lastClickAt.current = 0;
4831
- applyPos(null);
4055
+ applyPos(null, true);
4832
4056
  return;
4833
4057
  }
4834
4058
  lastClickAt.current = now;
@@ -4846,7 +4070,7 @@ function Badge() {
4846
4070
  node.offsetHeight
4847
4071
  );
4848
4072
  node.style.translate = `${snapped.x}px ${snapped.y}px`;
4849
- applyPos(snapped);
4073
+ applyPos(snapped, true);
4850
4074
  };
4851
4075
  window.addEventListener("pointermove", onMove, { passive: false });
4852
4076
  window.addEventListener("pointerup", onUp);
@@ -4865,15 +4089,18 @@ function Badge() {
4865
4089
  "aria-expanded": panelOpen,
4866
4090
  onPointerDown,
4867
4091
  children: [
4868
- /* @__PURE__ */ jsx("span", { className: "rk-badge-dot" }),
4092
+ /* @__PURE__ */ jsx2("span", { className: "rk-badge-dot" }),
4869
4093
  "Review",
4870
- openCount > 0 && /* @__PURE__ */ jsx("span", { className: "rk-badge-count", children: openCount })
4094
+ openCount > 0 && /* @__PURE__ */ jsx2("span", { className: "rk-badge-count", children: openCount })
4871
4095
  ]
4872
4096
  }
4873
4097
  );
4874
4098
  }
4099
+
4100
+ // src/client/components/icons.tsx
4101
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
4875
4102
  function Base({ size = 16, children, ...rest }) {
4876
- return /* @__PURE__ */ jsx(
4103
+ return /* @__PURE__ */ jsx3(
4877
4104
  "svg",
4878
4105
  {
4879
4106
  width: size,
@@ -4892,170 +4119,177 @@ function Base({ size = 16, children, ...rest }) {
4892
4119
  );
4893
4120
  }
4894
4121
  function IconText(props) {
4895
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4896
- /* @__PURE__ */ jsx("path", { d: "M3.5 4.5V3h9v1.5" }),
4897
- /* @__PURE__ */ jsx("path", { d: "M8 3v10" }),
4898
- /* @__PURE__ */ jsx("path", { d: "M6 13h4" })
4122
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4123
+ /* @__PURE__ */ jsx3("path", { d: "M3.5 4.5V3h9v1.5" }),
4124
+ /* @__PURE__ */ jsx3("path", { d: "M8 3v10" }),
4125
+ /* @__PURE__ */ jsx3("path", { d: "M6 13h4" })
4899
4126
  ] });
4900
4127
  }
4901
4128
  function IconImage(props) {
4902
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4903
- /* @__PURE__ */ jsx("rect", { x: "2.25", y: "3.25", width: "11.5", height: "9.5", rx: "1.5" }),
4904
- /* @__PURE__ */ jsx("circle", { cx: "5.75", cy: "6.5", r: "1", fill: "currentColor", stroke: "none" }),
4905
- /* @__PURE__ */ jsx("path", { d: "M2.5 11l3.2-3.2a1 1 0 0 1 1.4 0L10 10.7l1.4-1.4a1 1 0 0 1 1.4 0l.95.95" })
4129
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4130
+ /* @__PURE__ */ jsx3("rect", { x: "2.25", y: "3.25", width: "11.5", height: "9.5", rx: "1.5" }),
4131
+ /* @__PURE__ */ jsx3("circle", { cx: "5.75", cy: "6.5", r: "1", fill: "currentColor", stroke: "none" }),
4132
+ /* @__PURE__ */ jsx3("path", { d: "M2.5 11l3.2-3.2a1 1 0 0 1 1.4 0L10 10.7l1.4-1.4a1 1 0 0 1 1.4 0l.95.95" })
4906
4133
  ] });
4907
4134
  }
4908
4135
  function IconComment(props) {
4909
- return /* @__PURE__ */ jsx(Base, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M13.5 7.6c0 2.7-2.46 4.6-5.5 4.6-.6 0-1.18-.07-1.72-.2L3.5 13.5l.55-2.2C3.06 10.45 2.5 9.1 2.5 7.6 2.5 4.9 4.96 3 8 3s5.5 1.9 5.5 4.6Z" }) });
4136
+ return /* @__PURE__ */ jsx3(Base, { ...props, children: /* @__PURE__ */ jsx3("path", { d: "M13.5 7.6c0 2.7-2.46 4.6-5.5 4.6-.6 0-1.18-.07-1.72-.2L3.5 13.5l.55-2.2C3.06 10.45 2.5 9.1 2.5 7.6 2.5 4.9 4.96 3 8 3s5.5 1.9 5.5 4.6Z" }) });
4910
4137
  }
4911
4138
  function IconTrash(props) {
4912
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4913
- /* @__PURE__ */ jsx("path", { d: "M2.75 4.25h10.5" }),
4914
- /* @__PURE__ */ jsx("path", { d: "M5.5 4.25V3.4c0-.63.51-1.15 1.15-1.15h2.7c.64 0 1.15.52 1.15 1.15v.85" }),
4915
- /* @__PURE__ */ jsx("path", { d: "M4.25 4.5l.55 8.05c.04.62.56 1.1 1.18 1.1h4.04c.62 0 1.14-.48 1.18-1.1l.55-8.05" }),
4916
- /* @__PURE__ */ jsx("path", { d: "M6.6 7v3.8M9.4 7v3.8" })
4139
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4140
+ /* @__PURE__ */ jsx3("path", { d: "M2.75 4.25h10.5" }),
4141
+ /* @__PURE__ */ jsx3("path", { d: "M5.5 4.25V3.4c0-.63.51-1.15 1.15-1.15h2.7c.64 0 1.15.52 1.15 1.15v.85" }),
4142
+ /* @__PURE__ */ jsx3("path", { d: "M4.25 4.5l.55 8.05c.04.62.56 1.1 1.18 1.1h4.04c.62 0 1.14-.48 1.18-1.1l.55-8.05" }),
4143
+ /* @__PURE__ */ jsx3("path", { d: "M6.6 7v3.8M9.4 7v3.8" })
4917
4144
  ] });
4918
4145
  }
4919
4146
  function IconMove(props) {
4920
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4921
- /* @__PURE__ */ jsx("path", { d: "M8 2.5v11" }),
4922
- /* @__PURE__ */ jsx("path", { d: "M5.25 5.25 8 2.5l2.75 2.75" }),
4923
- /* @__PURE__ */ jsx("path", { d: "M5.25 10.75 8 13.5l2.75-2.75" })
4147
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4148
+ /* @__PURE__ */ jsx3("path", { d: "M8 2.5v11" }),
4149
+ /* @__PURE__ */ jsx3("path", { d: "M5.25 5.25 8 2.5l2.75 2.75" }),
4150
+ /* @__PURE__ */ jsx3("path", { d: "M5.25 10.75 8 13.5l2.75-2.75" })
4924
4151
  ] });
4925
4152
  }
4926
4153
  function IconStyle(props) {
4927
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4928
- /* @__PURE__ */ jsx("path", { d: "M11.2 2.6a1.6 1.6 0 0 1 2.26 2.26L7.1 11.2l-3.1.85.84-3.1 6.36-6.35Z" }),
4929
- /* @__PURE__ */ jsx("path", { d: "M9.6 4.2l2.2 2.2" })
4154
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4155
+ /* @__PURE__ */ jsx3("path", { d: "M11.2 2.6a1.6 1.6 0 0 1 2.26 2.26L7.1 11.2l-3.1.85.84-3.1 6.36-6.35Z" }),
4156
+ /* @__PURE__ */ jsx3("path", { d: "M9.6 4.2l2.2 2.2" })
4930
4157
  ] });
4931
4158
  }
4932
4159
  function IconClose(props) {
4933
- return /* @__PURE__ */ jsx(Base, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" }) });
4160
+ return /* @__PURE__ */ jsx3(Base, { ...props, children: /* @__PURE__ */ jsx3("path", { d: "M4 4l8 8M12 4l-8 8" }) });
4934
4161
  }
4935
4162
  function IconSearch(props) {
4936
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4937
- /* @__PURE__ */ jsx("circle", { cx: "7", cy: "7", r: "4.25" }),
4938
- /* @__PURE__ */ jsx("path", { d: "M10.3 10.3 13.5 13.5" })
4163
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4164
+ /* @__PURE__ */ jsx3("circle", { cx: "7", cy: "7", r: "4.25" }),
4165
+ /* @__PURE__ */ jsx3("path", { d: "M10.3 10.3 13.5 13.5" })
4939
4166
  ] });
4940
4167
  }
4941
4168
  function IconDownload(props) {
4942
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4943
- /* @__PURE__ */ jsx("path", { d: "M8 2.5v7.5" }),
4944
- /* @__PURE__ */ jsx("path", { d: "M5.25 7.5 8 10.25 10.75 7.5" }),
4945
- /* @__PURE__ */ jsx("path", { d: "M3 11.5v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1" })
4169
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4170
+ /* @__PURE__ */ jsx3("path", { d: "M8 2.5v7.5" }),
4171
+ /* @__PURE__ */ jsx3("path", { d: "M5.25 7.5 8 10.25 10.75 7.5" }),
4172
+ /* @__PURE__ */ jsx3("path", { d: "M3 11.5v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1" })
4946
4173
  ] });
4947
4174
  }
4948
4175
  function IconUpload(props) {
4949
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4950
- /* @__PURE__ */ jsx("path", { d: "M8 10V2.75" }),
4951
- /* @__PURE__ */ jsx("path", { d: "M5.25 5.25 8 2.5l2.75 2.75" }),
4952
- /* @__PURE__ */ jsx("path", { d: "M3 11.5v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1" })
4176
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4177
+ /* @__PURE__ */ jsx3("path", { d: "M8 10V2.75" }),
4178
+ /* @__PURE__ */ jsx3("path", { d: "M5.25 5.25 8 2.5l2.75 2.75" }),
4179
+ /* @__PURE__ */ jsx3("path", { d: "M3 11.5v1a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1" })
4953
4180
  ] });
4954
4181
  }
4955
4182
  function IconCopyClipboard(props) {
4956
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4957
- /* @__PURE__ */ jsx("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
4958
- /* @__PURE__ */ jsx("path", { d: "M10.5 5.5v-2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2" })
4183
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4184
+ /* @__PURE__ */ jsx3("rect", { x: "5.5", y: "5.5", width: "8", height: "8", rx: "1.5" }),
4185
+ /* @__PURE__ */ jsx3("path", { d: "M10.5 5.5v-2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2" })
4959
4186
  ] });
4960
4187
  }
4961
4188
  function IconUndo(props) {
4962
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4963
- /* @__PURE__ */ jsx("path", { d: "M3 6.5h6.5a3.5 3.5 0 0 1 0 7H6" }),
4964
- /* @__PURE__ */ jsx("path", { d: "M5.5 4 3 6.5 5.5 9" })
4189
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4190
+ /* @__PURE__ */ jsx3("path", { d: "M3 6.5h6.5a3.5 3.5 0 0 1 0 7H6" }),
4191
+ /* @__PURE__ */ jsx3("path", { d: "M5.5 4 3 6.5 5.5 9" })
4965
4192
  ] });
4966
4193
  }
4967
4194
  function IconCheck(props) {
4968
- return /* @__PURE__ */ jsx(Base, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M3 8.5 6.5 12 13 4.5" }) });
4195
+ return /* @__PURE__ */ jsx3(Base, { ...props, children: /* @__PURE__ */ jsx3("path", { d: "M3 8.5 6.5 12 13 4.5" }) });
4969
4196
  }
4970
4197
  function IconWarning(props) {
4971
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4972
- /* @__PURE__ */ jsx("path", { d: "M8 2.75 14.5 13.5h-13L8 2.75Z" }),
4973
- /* @__PURE__ */ jsx("path", { d: "M8 7v3" }),
4974
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "11.9", r: "0.55", fill: "currentColor", stroke: "none" })
4198
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4199
+ /* @__PURE__ */ jsx3("path", { d: "M8 2.75 14.5 13.5h-13L8 2.75Z" }),
4200
+ /* @__PURE__ */ jsx3("path", { d: "M8 7v3" }),
4201
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "11.9", r: "0.55", fill: "currentColor", stroke: "none" })
4975
4202
  ] });
4976
4203
  }
4977
4204
  function IconTarget(props) {
4978
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4979
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "5.5" }),
4980
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "1", fill: "currentColor", stroke: "none" }),
4981
- /* @__PURE__ */ jsx("path", { d: "M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2" })
4205
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4206
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "5.5" }),
4207
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "1", fill: "currentColor", stroke: "none" }),
4208
+ /* @__PURE__ */ jsx3("path", { d: "M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2" })
4982
4209
  ] });
4983
4210
  }
4984
4211
  function IconKeyboard(props) {
4985
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4986
- /* @__PURE__ */ jsx("rect", { x: "2", y: "4.5", width: "12", height: "7.5", rx: "1.5" }),
4987
- /* @__PURE__ */ jsx("path", { d: "M4.5 7h.01M7 7h.01M9.5 7h.01M12 7h.01M5 9.5h6" })
4212
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4213
+ /* @__PURE__ */ jsx3("rect", { x: "2", y: "4.5", width: "12", height: "7.5", rx: "1.5" }),
4214
+ /* @__PURE__ */ jsx3("path", { d: "M4.5 7h.01M7 7h.01M9.5 7h.01M12 7h.01M5 9.5h6" })
4988
4215
  ] });
4989
4216
  }
4990
4217
  function IconGear(props) {
4991
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
4992
- /* @__PURE__ */ jsx("circle", { cx: "8", cy: "8", r: "2.1" }),
4993
- /* @__PURE__ */ jsx("path", { d: "M8 1.8v2M8 12.2v2M1.8 8h2M12.2 8h2M3.6 3.6l1.4 1.4M11 11l1.4 1.4M12.4 3.6 11 5M5 11l-1.4 1.4" })
4218
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4219
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "2.1" }),
4220
+ /* @__PURE__ */ jsx3("path", { d: "M8 1.8v2M8 12.2v2M1.8 8h2M12.2 8h2M3.6 3.6l1.4 1.4M11 11l1.4 1.4M12.4 3.6 11 5M5 11l-1.4 1.4" })
4994
4221
  ] });
4995
4222
  }
4996
4223
  function IconFilter(props) {
4997
- return /* @__PURE__ */ jsx(Base, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M2.5 4h11M4.5 8h7M6.5 12h3" }) });
4224
+ return /* @__PURE__ */ jsx3(Base, { ...props, children: /* @__PURE__ */ jsx3("path", { d: "M2.5 4h11M4.5 8h7M6.5 12h3" }) });
4998
4225
  }
4999
4226
  function IconLink(props) {
5000
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
5001
- /* @__PURE__ */ jsx("path", { d: "M6.5 9.5 9.5 6.5" }),
5002
- /* @__PURE__ */ jsx("path", { d: "M7.25 4.9 8.4 3.75a2.4 2.4 0 0 1 3.4 0 2.4 2.4 0 0 1 0 3.4L10.65 8.3" }),
5003
- /* @__PURE__ */ jsx("path", { d: "M8.75 11.1 7.6 12.25a2.4 2.4 0 0 1-3.4 0 2.4 2.4 0 0 1 0-3.4L5.35 7.7" })
4227
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4228
+ /* @__PURE__ */ jsx3("path", { d: "M6.5 9.5 9.5 6.5" }),
4229
+ /* @__PURE__ */ jsx3("path", { d: "M7.25 4.9 8.4 3.75a2.4 2.4 0 0 1 3.4 0 2.4 2.4 0 0 1 0 3.4L10.65 8.3" }),
4230
+ /* @__PURE__ */ jsx3("path", { d: "M8.75 11.1 7.6 12.25a2.4 2.4 0 0 1-3.4 0 2.4 2.4 0 0 1 0-3.4L5.35 7.7" })
5004
4231
  ] });
5005
4232
  }
5006
4233
  function IconUnlink(props) {
5007
- return /* @__PURE__ */ jsxs(Base, { ...props, children: [
5008
- /* @__PURE__ */ jsx("path", { d: "M6.5 4.5v-2M9.5 13.5v-2M4.5 6.5h-2M13.5 9.5h-2" }),
5009
- /* @__PURE__ */ jsx("path", { d: "M6 10 3.5 12.5M10 6l2.5-2.5" })
4234
+ return /* @__PURE__ */ jsxs2(Base, { ...props, children: [
4235
+ /* @__PURE__ */ jsx3("path", { d: "M6.5 4.5v-2M9.5 13.5v-2M4.5 6.5h-2M13.5 9.5h-2" }),
4236
+ /* @__PURE__ */ jsx3("path", { d: "M6 10 3.5 12.5M10 6l2.5-2.5" })
5010
4237
  ] });
5011
4238
  }
5012
4239
  function iconForType(type, size = 14) {
5013
4240
  switch (type) {
5014
4241
  case "copy":
5015
- return /* @__PURE__ */ jsx(IconText, { size });
4242
+ return /* @__PURE__ */ jsx3(IconText, { size });
5016
4243
  case "image":
5017
- return /* @__PURE__ */ jsx(IconImage, { size });
4244
+ return /* @__PURE__ */ jsx3(IconImage, { size });
5018
4245
  case "comment":
5019
- return /* @__PURE__ */ jsx(IconComment, { size });
4246
+ return /* @__PURE__ */ jsx3(IconComment, { size });
5020
4247
  case "delete":
5021
- return /* @__PURE__ */ jsx(IconTrash, { size });
4248
+ return /* @__PURE__ */ jsx3(IconTrash, { size });
5022
4249
  case "move":
5023
- return /* @__PURE__ */ jsx(IconMove, { size });
4250
+ return /* @__PURE__ */ jsx3(IconMove, { size });
5024
4251
  case "style":
5025
- return /* @__PURE__ */ jsx(IconStyle, { size });
4252
+ return /* @__PURE__ */ jsx3(IconStyle, { size });
5026
4253
  default:
5027
- return /* @__PURE__ */ jsx(IconTarget, { size });
4254
+ return /* @__PURE__ */ jsx3(IconTarget, { size });
5028
4255
  }
5029
4256
  }
4257
+
4258
+ // src/client/components/CheatSheet.tsx
4259
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
5030
4260
  var GROUPS = ["Tools", "View", "Items", "Session"];
5031
4261
  function CheatSheet() {
5032
4262
  const { cheatsheetOpen } = useStore();
5033
4263
  const dispatch = useDispatch();
5034
4264
  if (!cheatsheetOpen) return null;
5035
4265
  const close = () => dispatch({ type: "SET_CHEATSHEET", open: false });
5036
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5037
- /* @__PURE__ */ jsx("div", { className: "rk-backdrop", onClick: close }),
5038
- /* @__PURE__ */ jsxs("div", { className: "rk-modal rk-cheatsheet", role: "dialog", "aria-modal": "true", "aria-label": "Keyboard shortcuts", children: [
5039
- /* @__PURE__ */ jsxs("div", { className: "rk-cheat-head", children: [
5040
- /* @__PURE__ */ jsx("span", { className: "rk-cheat-head-icon", children: /* @__PURE__ */ jsx(IconKeyboard, { size: 15 }) }),
5041
- /* @__PURE__ */ jsx("span", { className: "rk-cheat-title", children: "Keyboard shortcuts" }),
5042
- /* @__PURE__ */ jsx("span", { style: { flex: 1 } }),
5043
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ jsx(IconClose, { size: 13 }) })
4266
+ return /* @__PURE__ */ jsxs3(Fragment, { children: [
4267
+ /* @__PURE__ */ jsx4("div", { className: "rk-backdrop", onClick: close }),
4268
+ /* @__PURE__ */ jsxs3("div", { className: "rk-modal rk-cheatsheet", role: "dialog", "aria-modal": "true", "aria-label": "Keyboard shortcuts", children: [
4269
+ /* @__PURE__ */ jsxs3("div", { className: "rk-cheat-head", children: [
4270
+ /* @__PURE__ */ jsx4("span", { className: "rk-cheat-head-icon", children: /* @__PURE__ */ jsx4(IconKeyboard, { size: 15 }) }),
4271
+ /* @__PURE__ */ jsx4("span", { className: "rk-cheat-title", children: "Keyboard shortcuts" }),
4272
+ /* @__PURE__ */ jsx4("span", { style: { flex: 1 } }),
4273
+ /* @__PURE__ */ jsx4("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ jsx4(IconClose, { size: 13 }) })
5044
4274
  ] }),
5045
- /* @__PURE__ */ jsx("div", { className: "rk-cheat-grid", children: GROUPS.map((group) => /* @__PURE__ */ jsxs("section", { className: "rk-cheat-group", children: [
5046
- /* @__PURE__ */ jsx("h3", { className: "rk-cheat-group-title", children: group }),
5047
- SHORTCUTS.filter((def) => def.group === group).map((def) => /* @__PURE__ */ jsxs("div", { className: "rk-shortcut-row", children: [
5048
- /* @__PURE__ */ jsxs("span", { className: "rk-shortcut-text", children: [
5049
- /* @__PURE__ */ jsx("span", { className: "rk-shortcut-title", children: def.title }),
5050
- /* @__PURE__ */ jsx("span", { className: "rk-shortcut-desc", children: def.description })
4275
+ /* @__PURE__ */ jsx4("div", { className: "rk-cheat-grid", children: GROUPS.map((group) => /* @__PURE__ */ jsxs3("section", { className: "rk-cheat-group", children: [
4276
+ /* @__PURE__ */ jsx4("h3", { className: "rk-cheat-group-title", children: group }),
4277
+ SHORTCUTS.filter((def) => def.group === group).map((def) => /* @__PURE__ */ jsxs3("div", { className: "rk-shortcut-row", children: [
4278
+ /* @__PURE__ */ jsxs3("span", { className: "rk-shortcut-text", children: [
4279
+ /* @__PURE__ */ jsx4("span", { className: "rk-shortcut-title", children: def.title }),
4280
+ /* @__PURE__ */ jsx4("span", { className: "rk-shortcut-desc", children: def.description })
5051
4281
  ] }),
5052
- /* @__PURE__ */ jsx("span", { className: "rk-keys", children: displayKeys(def, IS_MAC).map((key) => /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: key }, key)) })
4282
+ /* @__PURE__ */ jsx4("span", { className: "rk-keys", children: displayKeys(def, IS_MAC).map((key) => /* @__PURE__ */ jsx4("span", { className: "rk-kbd", children: key }, key)) })
5053
4283
  ] }, def.id))
5054
4284
  ] }, group)) }),
5055
- /* @__PURE__ */ jsx("div", { className: "rk-egg", children: "Psst \u2014 the \u201C/\u201D box knows a few tricks it won\u2019t tell you about." })
4285
+ /* @__PURE__ */ jsx4("div", { className: "rk-egg", children: "Psst \u2014 the \u201C/\u201D box knows a few tricks it won\u2019t tell you about." })
5056
4286
  ] })
5057
4287
  ] });
5058
4288
  }
4289
+
4290
+ // src/client/components/EmblemaConnect.tsx
4291
+ import { useEffect as useEffect4, useState as useState2 } from "react";
4292
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
5059
4293
  function shortKey(key) {
5060
4294
  return `${key.slice(0, 4)}\u2026${key.slice(-4)}`;
5061
4295
  }
@@ -5070,14 +4304,14 @@ function ago(iso) {
5070
4304
  function EmblemaConnect() {
5071
4305
  const { emblemaConnectOpen } = useStore();
5072
4306
  const dispatch = useDispatch();
5073
- const [state, setState] = useState("loading");
5074
- const [key, setKey] = useState(null);
5075
- const [busy, setBusy] = useState(false);
4307
+ const [state, setState] = useState2("loading");
4308
+ const [key, setKey] = useState2(null);
4309
+ const [busy, setBusy] = useState2(false);
5076
4310
  const refresh = () => {
5077
4311
  setState("loading");
5078
4312
  void fetchSyncState().then(setState);
5079
4313
  };
5080
- useEffect(() => {
4314
+ useEffect4(() => {
5081
4315
  if (emblemaConnectOpen) {
5082
4316
  setKey(null);
5083
4317
  refresh();
@@ -5106,48 +4340,48 @@ function EmblemaConnect() {
5106
4340
  refresh();
5107
4341
  }).finally(() => setBusy(false));
5108
4342
  };
5109
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5110
- /* @__PURE__ */ jsx("div", { className: "rk-backdrop", onClick: close }),
5111
- /* @__PURE__ */ jsx("div", { className: "rk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Connect to Emblema", children: /* @__PURE__ */ jsxs("div", { className: "rk-modal-body", children: [
5112
- /* @__PURE__ */ jsxs("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4343
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
4344
+ /* @__PURE__ */ jsx5("div", { className: "rk-backdrop", onClick: close }),
4345
+ /* @__PURE__ */ jsx5("div", { className: "rk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Connect to Emblema", children: /* @__PURE__ */ jsxs4("div", { className: "rk-modal-body", children: [
4346
+ /* @__PURE__ */ jsxs4("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5113
4347
  "Connect to Emblema",
5114
- /* @__PURE__ */ jsx("span", { style: { flex: 1 } }),
5115
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ jsx(IconClose, { size: 13 }) })
4348
+ /* @__PURE__ */ jsx5("span", { style: { flex: 1 } }),
4349
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "rk-icon-btn", "aria-label": "Close", onClick: close, autoFocus: true, children: /* @__PURE__ */ jsx5(IconClose, { size: 13 }) })
5116
4350
  ] }),
5117
- state === "loading" && /* @__PURE__ */ jsx("div", { className: "rk-modal-text", children: "Looking for the local sync agent\u2026" }),
5118
- state === null && /* @__PURE__ */ jsxs("div", { className: "rk-modal-text", children: [
4351
+ state === "loading" && /* @__PURE__ */ jsx5("div", { className: "rk-modal-text", children: "Looking for the local sync agent\u2026" }),
4352
+ state === null && /* @__PURE__ */ jsxs4("div", { className: "rk-modal-text", children: [
5119
4353
  "The local sync agent isn't running. In the project repo run:",
5120
- /* @__PURE__ */ jsxs("code", { className: "rk-connect-cmd", children: [
5121
- "npx reviewkit-emblema init --origin ",
4354
+ /* @__PURE__ */ jsxs4("code", { className: "rk-connect-cmd", children: [
4355
+ "npx @satanwagen/reviewkit-cli init --origin ",
5122
4356
  window.location.origin
5123
4357
  ] }),
5124
- /* @__PURE__ */ jsx("code", { className: "rk-connect-cmd", children: "npx reviewkit-emblema serve" }),
4358
+ /* @__PURE__ */ jsx5("code", { className: "rk-connect-cmd", children: "npx @satanwagen/reviewkit-cli serve" }),
5125
4359
  "then reopen this dialog."
5126
4360
  ] }),
5127
- state !== null && state !== "loading" && /* @__PURE__ */ jsxs(Fragment, { children: [
5128
- /* @__PURE__ */ jsxs("div", { className: "rk-modal-text", children: [
4361
+ state !== null && state !== "loading" && /* @__PURE__ */ jsxs4(Fragment2, { children: [
4362
+ /* @__PURE__ */ jsxs4("div", { className: "rk-modal-text", children: [
5129
4363
  "Project ",
5130
- /* @__PURE__ */ jsx("strong", { children: state.name }),
4364
+ /* @__PURE__ */ jsx5("strong", { children: state.name }),
5131
4365
  " ",
5132
- /* @__PURE__ */ jsxs("span", { className: "rk-connect-id", children: [
4366
+ /* @__PURE__ */ jsxs4("span", { className: "rk-connect-id", children: [
5133
4367
  "(",
5134
4368
  state.id,
5135
4369
  ")"
5136
4370
  ] }),
5137
- /* @__PURE__ */ jsx("br", {}),
4371
+ /* @__PURE__ */ jsx5("br", {}),
5138
4372
  "Status:",
5139
4373
  " ",
5140
- state.revoked ? /* @__PURE__ */ jsx("span", { className: "rk-connect-bad", children: "revoked \u2014 generate a new key to reconnect" }) : /* @__PURE__ */ jsx(Fragment, { children: "paired key active" }),
5141
- /* @__PURE__ */ jsx("br", {}),
4374
+ state.revoked ? /* @__PURE__ */ jsx5("span", { className: "rk-connect-bad", children: "revoked \u2014 generate a new key to reconnect" }) : /* @__PURE__ */ jsx5(Fragment2, { children: "paired key active" }),
4375
+ /* @__PURE__ */ jsx5("br", {}),
5142
4376
  "Last confirmed by Emblema: ",
5143
- /* @__PURE__ */ jsx("strong", { children: ago(state.lastAck) }),
4377
+ /* @__PURE__ */ jsx5("strong", { children: ago(state.lastAck) }),
5144
4378
  " \xB7 ",
5145
4379
  state.findings,
5146
4380
  " finding",
5147
4381
  state.findings === 1 ? "" : "s",
5148
4382
  " tracked"
5149
4383
  ] }),
5150
- key && /* @__PURE__ */ jsx(
4384
+ key && /* @__PURE__ */ jsx5(
5151
4385
  "code",
5152
4386
  {
5153
4387
  className: "rk-connect-key",
@@ -5155,8 +4389,8 @@ function EmblemaConnect() {
5155
4389
  children: shortKey(key)
5156
4390
  }
5157
4391
  ),
5158
- /* @__PURE__ */ jsxs("div", { className: "rk-connect-actions", children: [
5159
- /* @__PURE__ */ jsx(
4392
+ /* @__PURE__ */ jsxs4("div", { className: "rk-connect-actions", children: [
4393
+ /* @__PURE__ */ jsx5(
5160
4394
  "button",
5161
4395
  {
5162
4396
  type: "button",
@@ -5167,7 +4401,7 @@ function EmblemaConnect() {
5167
4401
  children: state.revoked ? "Generate new key" : "New key (rotate)"
5168
4402
  }
5169
4403
  ),
5170
- /* @__PURE__ */ jsx(
4404
+ /* @__PURE__ */ jsx5(
5171
4405
  "button",
5172
4406
  {
5173
4407
  type: "button",
@@ -5184,8 +4418,8 @@ function EmblemaConnect() {
5184
4418
  children: "Copy key"
5185
4419
  }
5186
4420
  ),
5187
- /* @__PURE__ */ jsx("span", { style: { flex: 1 } }),
5188
- /* @__PURE__ */ jsx(
4421
+ /* @__PURE__ */ jsx5("span", { style: { flex: 1 } }),
4422
+ /* @__PURE__ */ jsx5(
5189
4423
  "button",
5190
4424
  {
5191
4425
  type: "button",
@@ -5207,12 +4441,16 @@ function EmblemaConnect() {
5207
4441
  ] }) })
5208
4442
  ] });
5209
4443
  }
4444
+
4445
+ // src/client/components/CoachMark.tsx
4446
+ import { useState as useState3 } from "react";
4447
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
5210
4448
  var COACH_KEY = "review-kit:coach-dismissed";
5211
4449
  function CoachMark() {
5212
4450
  const { coachOpen, session } = useStore();
5213
4451
  const dispatch = useDispatch();
5214
4452
  const config = useConfig();
5215
- const [name, setName] = useState(session?.author ?? config.defaultAuthor ?? "");
4453
+ const [name, setName] = useState3(session?.author ?? config.defaultAuthor ?? "");
5216
4454
  if (!coachOpen) return null;
5217
4455
  const done = (startPicking) => {
5218
4456
  if (name.trim() !== "") dispatch({ type: "SET_AUTHOR", author: name.trim() });
@@ -5223,39 +4461,39 @@ function CoachMark() {
5223
4461
  }
5224
4462
  if (startPicking) dispatch({ type: "SET_MODE", mode: "pick" });
5225
4463
  };
5226
- return /* @__PURE__ */ jsxs("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
5227
- /* @__PURE__ */ jsx("div", { className: "rk-coach-accent" }),
5228
- /* @__PURE__ */ jsxs("div", { className: "rk-coach-body", children: [
5229
- /* @__PURE__ */ jsxs("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5230
- /* @__PURE__ */ jsx(IconTarget, { size: 16 }),
4464
+ return /* @__PURE__ */ jsxs5("div", { className: "rk-coach", role: "dialog", "aria-label": "Welcome to review mode", children: [
4465
+ /* @__PURE__ */ jsx6("div", { className: "rk-coach-accent" }),
4466
+ /* @__PURE__ */ jsxs5("div", { className: "rk-coach-body", children: [
4467
+ /* @__PURE__ */ jsxs5("div", { className: "rk-modal-title", style: { display: "flex", alignItems: "center", gap: 8 }, children: [
4468
+ /* @__PURE__ */ jsx6(IconTarget, { size: 16 }),
5231
4469
  " Review mode"
5232
4470
  ] }),
5233
- /* @__PURE__ */ jsx("div", { className: "rk-modal-text", children: "Click anything on the page to suggest a change \u2014 rewrite the copy, swap an image, or leave a note. The live site stays untouched; everything you add is collected for the team." }),
5234
- /* @__PURE__ */ jsxs("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
5235
- /* @__PURE__ */ jsxs("span", { children: [
5236
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "T" }),
4471
+ /* @__PURE__ */ jsx6("div", { className: "rk-modal-text", children: "Click anything on the page to suggest a change \u2014 rewrite the copy, swap an image, or leave a note. The live site stays untouched; everything you add is collected for the team." }),
4472
+ /* @__PURE__ */ jsxs5("div", { className: "rk-coach-keys", "aria-label": "Key shortcuts", children: [
4473
+ /* @__PURE__ */ jsxs5("span", { children: [
4474
+ /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "T" }),
5237
4475
  " rewrite"
5238
4476
  ] }),
5239
- /* @__PURE__ */ jsxs("span", { children: [
5240
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "C" }),
4477
+ /* @__PURE__ */ jsxs5("span", { children: [
4478
+ /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "C" }),
5241
4479
  " comment"
5242
4480
  ] }),
5243
- /* @__PURE__ */ jsxs("span", { children: [
5244
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "I" }),
4481
+ /* @__PURE__ */ jsxs5("span", { children: [
4482
+ /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "I" }),
5245
4483
  " image"
5246
4484
  ] }),
5247
- /* @__PURE__ */ jsxs("span", { children: [
5248
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "V" }),
4485
+ /* @__PURE__ */ jsxs5("span", { children: [
4486
+ /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "V" }),
5249
4487
  " select"
5250
4488
  ] }),
5251
- /* @__PURE__ */ jsxs("span", { children: [
5252
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "?" }),
4489
+ /* @__PURE__ */ jsxs5("span", { children: [
4490
+ /* @__PURE__ */ jsx6("span", { className: "rk-kbd", children: "?" }),
5253
4491
  " all shortcuts"
5254
4492
  ] })
5255
4493
  ] }),
5256
- /* @__PURE__ */ jsxs("div", { children: [
5257
- /* @__PURE__ */ jsx("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
5258
- /* @__PURE__ */ jsx(
4494
+ /* @__PURE__ */ jsxs5("div", { children: [
4495
+ /* @__PURE__ */ jsx6("label", { className: "rk-label", htmlFor: "rk-author", children: "Your name" }),
4496
+ /* @__PURE__ */ jsx6(
5259
4497
  "input",
5260
4498
  {
5261
4499
  id: "rk-author",
@@ -5269,32 +4507,36 @@ function CoachMark() {
5269
4507
  }
5270
4508
  )
5271
4509
  ] }),
5272
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
5273
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
5274
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
4510
+ /* @__PURE__ */ jsxs5("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 4 }, children: [
4511
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => done(false), children: "Not now" }),
4512
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: () => done(true), children: "Start reviewing" })
5275
4513
  ] })
5276
4514
  ] })
5277
4515
  ] });
5278
4516
  }
4517
+
4518
+ // src/client/components/Confirm.tsx
4519
+ import { useEffect as useEffect5, useRef as useRef5 } from "react";
4520
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
5279
4521
  function ConfirmHost() {
5280
4522
  const { confirm } = useStore();
5281
4523
  const dispatch = useDispatch();
5282
- const cancelRef = useRef(null);
5283
- useEffect(() => {
4524
+ const cancelRef = useRef5(null);
4525
+ useEffect5(() => {
5284
4526
  if (confirm) cancelRef.current?.focus();
5285
4527
  }, [confirm]);
5286
4528
  if (!confirm) return null;
5287
4529
  const close = () => dispatch({ type: "SET_CONFIRM", confirm: null });
5288
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5289
- /* @__PURE__ */ jsx("div", { className: "rk-backdrop", onClick: close }),
5290
- /* @__PURE__ */ jsxs("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
5291
- /* @__PURE__ */ jsxs("div", { className: "rk-modal-body", children: [
5292
- /* @__PURE__ */ jsx("div", { className: "rk-modal-title", children: confirm.title }),
5293
- /* @__PURE__ */ jsx("div", { className: "rk-modal-text", children: confirm.message })
4530
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
4531
+ /* @__PURE__ */ jsx7("div", { className: "rk-backdrop", onClick: close }),
4532
+ /* @__PURE__ */ jsxs6("div", { className: "rk-modal", role: "alertdialog", "aria-modal": "true", "aria-label": confirm.title, children: [
4533
+ /* @__PURE__ */ jsxs6("div", { className: "rk-modal-body", children: [
4534
+ /* @__PURE__ */ jsx7("div", { className: "rk-modal-title", children: confirm.title }),
4535
+ /* @__PURE__ */ jsx7("div", { className: "rk-modal-text", children: confirm.message })
5294
4536
  ] }),
5295
- /* @__PURE__ */ jsxs("div", { className: "rk-modal-actions", children: [
5296
- /* @__PURE__ */ jsx("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
5297
- /* @__PURE__ */ jsx(
4537
+ /* @__PURE__ */ jsxs6("div", { className: "rk-modal-actions", children: [
4538
+ /* @__PURE__ */ jsx7("button", { ref: cancelRef, type: "button", className: "rk-btn rk-btn-ghost", onClick: close, children: "Cancel" }),
4539
+ /* @__PURE__ */ jsx7(
5298
4540
  "button",
5299
4541
  {
5300
4542
  type: "button",
@@ -5312,6 +4554,12 @@ function ConfirmHost() {
5312
4554
  ] });
5313
4555
  }
5314
4556
 
4557
+ // src/client/components/ImageEditor.tsx
4558
+ import { useEffect as useEffect6, useRef as useRef7, useState as useState5 } from "react";
4559
+
4560
+ // src/client/useAnchored.ts
4561
+ import { useLayoutEffect as useLayoutEffect2, useRef as useRef6, useState as useState4 } from "react";
4562
+
5315
4563
  // src/client/position.ts
5316
4564
  function place(anchor, size, side, offset) {
5317
4565
  switch (side) {
@@ -5355,9 +4603,9 @@ function computePosition(anchor, size, options = {}) {
5355
4603
 
5356
4604
  // src/client/useAnchored.ts
5357
4605
  function useAnchored(anchorEl, side = "bottom", offset = 8) {
5358
- const ref = useRef(null);
5359
- const [pos, setPos] = useState(null);
5360
- useLayoutEffect(() => {
4606
+ const ref = useRef6(null);
4607
+ const [pos, setPos] = useState4(null);
4608
+ useLayoutEffect2(() => {
5361
4609
  if (!anchorEl) {
5362
4610
  setPos(null);
5363
4611
  return;
@@ -5386,6 +4634,9 @@ function anchoredStyle(pos) {
5386
4634
  if (!pos) return { visibility: "hidden", transform: "translate3d(-9999px, -9999px, 0)" };
5387
4635
  return { transform: `translate3d(${pos.x}px, ${pos.y}px, 0)`, top: 0, left: 0 };
5388
4636
  }
4637
+
4638
+ // src/client/components/ImageEditor.tsx
4639
+ import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
5389
4640
  var MAX_BYTES = 2 * 1024 * 1024;
5390
4641
  var FETCH_TIMEOUT_MS = 12e3;
5391
4642
  var PROBE_TIMEOUT_MS = 8e3;
@@ -5443,15 +4694,15 @@ function urlFromDataTransfer(dt) {
5443
4694
  function ImageEditor({ editor }) {
5444
4695
  const dispatch = useDispatch();
5445
4696
  const { route, session } = useStore();
5446
- const [replacement, setReplacement] = useState(null);
5447
- const [urlValue, setUrlValue] = useState("");
5448
- const [loading, setLoading] = useState(false);
5449
- const [description, setDescription] = useState("");
5450
- const [priority, setPriority] = useState("nice");
5451
- const [over, setOver] = useState(false);
5452
- const fileInput = useRef(null);
5453
- const abortRef = useRef(null);
5454
- const debounceRef = useRef(null);
4697
+ const [replacement, setReplacement] = useState5(null);
4698
+ const [urlValue, setUrlValue] = useState5("");
4699
+ const [loading2, setLoading] = useState5(false);
4700
+ const [description, setDescription] = useState5("");
4701
+ const [priority, setPriority] = useState5("nice");
4702
+ const [over, setOver] = useState5(false);
4703
+ const fileInput = useRef7(null);
4704
+ const abortRef = useRef7(null);
4705
+ const debounceRef = useRef7(null);
5455
4706
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5456
4707
  const image = findImage(editor.element);
5457
4708
  const beforeSrc = image?.currentSrc || image?.src || "";
@@ -5515,7 +4766,7 @@ function ImageEditor({ editor }) {
5515
4766
  }
5516
4767
  }
5517
4768
  };
5518
- useEffect(() => {
4769
+ useEffect6(() => {
5519
4770
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
5520
4771
  if (urlValue.trim() === "" || replacement !== null) return;
5521
4772
  debounceRef.current = window.setTimeout(() => {
@@ -5525,8 +4776,8 @@ function ImageEditor({ editor }) {
5525
4776
  if (debounceRef.current !== null) window.clearTimeout(debounceRef.current);
5526
4777
  };
5527
4778
  }, [urlValue, replacement]);
5528
- useEffect(() => () => abortRef.current?.abort(), []);
5529
- useEffect(() => {
4779
+ useEffect6(() => () => abortRef.current?.abort(), []);
4780
+ useEffect6(() => {
5530
4781
  const onPaste = (event) => {
5531
4782
  const data = event.clipboardData;
5532
4783
  if (!data) return;
@@ -5604,23 +4855,23 @@ function ImageEditor({ editor }) {
5604
4855
  toast(dispatch, "Image proposal saved", "success");
5605
4856
  };
5606
4857
  const previewSrc = replacement?.kind === "data" ? replacement.dataUrl : replacement?.url;
5607
- return /* @__PURE__ */ jsxs("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
5608
- /* @__PURE__ */ jsxs("div", { className: "rk-card-head", children: [
5609
- /* @__PURE__ */ jsx(IconImage, { size: 14 }),
4858
+ return /* @__PURE__ */ jsxs7("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Replace image", children: [
4859
+ /* @__PURE__ */ jsxs7("div", { className: "rk-card-head", children: [
4860
+ /* @__PURE__ */ jsx8(IconImage, { size: 14 }),
5610
4861
  "Replace image"
5611
4862
  ] }),
5612
- /* @__PURE__ */ jsxs("div", { className: "rk-card-body", children: [
5613
- replacement && previewSrc ? /* @__PURE__ */ jsx("div", { className: "rk-thumbs", children: /* @__PURE__ */ jsxs("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
5614
- /* @__PURE__ */ jsxs("figure", { className: "rk-thumb", children: [
5615
- /* @__PURE__ */ jsx("img", { src: beforeSrc, alt: "Current" }),
5616
- /* @__PURE__ */ jsx("figcaption", { children: "Before" })
4863
+ /* @__PURE__ */ jsxs7("div", { className: "rk-card-body", children: [
4864
+ replacement && previewSrc ? /* @__PURE__ */ jsx8("div", { className: "rk-thumbs", children: /* @__PURE__ */ jsxs7("div", { className: "rk-thumb-pair", title: "Hover to compare with the current image", children: [
4865
+ /* @__PURE__ */ jsxs7("figure", { className: "rk-thumb", children: [
4866
+ /* @__PURE__ */ jsx8("img", { src: beforeSrc, alt: "Current" }),
4867
+ /* @__PURE__ */ jsx8("figcaption", { children: "Before" })
5617
4868
  ] }),
5618
- /* @__PURE__ */ jsxs("figure", { className: "rk-thumb", children: [
5619
- /* @__PURE__ */ jsx("img", { src: previewSrc, alt: "Proposed replacement" }),
5620
- /* @__PURE__ */ jsx("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
4869
+ /* @__PURE__ */ jsxs7("figure", { className: "rk-thumb", children: [
4870
+ /* @__PURE__ */ jsx8("img", { src: previewSrc, alt: "Proposed replacement" }),
4871
+ /* @__PURE__ */ jsx8("figcaption", { children: replacement.kind === "url" ? "After \xB7 linked" : "After" })
5621
4872
  ] })
5622
- ] }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5623
- /* @__PURE__ */ jsxs(
4873
+ ] }) }) : /* @__PURE__ */ jsxs7(Fragment4, { children: [
4874
+ /* @__PURE__ */ jsxs7(
5624
4875
  "button",
5625
4876
  {
5626
4877
  type: "button",
@@ -5633,18 +4884,18 @@ function ImageEditor({ editor }) {
5633
4884
  onDragLeave: () => setOver(false),
5634
4885
  onDrop,
5635
4886
  children: [
5636
- /* @__PURE__ */ jsx(IconUpload, { size: 18 }),
5637
- /* @__PURE__ */ jsxs("span", { children: [
4887
+ /* @__PURE__ */ jsx8(IconUpload, { size: 18 }),
4888
+ /* @__PURE__ */ jsxs7("span", { children: [
5638
4889
  "Drop an image \u2014 from your disk or another tab \u2014",
5639
- /* @__PURE__ */ jsx("br", {}),
4890
+ /* @__PURE__ */ jsx8("br", {}),
5640
4891
  "or click to browse (max 2 MB)"
5641
4892
  ] })
5642
4893
  ]
5643
4894
  }
5644
4895
  ),
5645
- /* @__PURE__ */ jsxs("div", { className: "rk-url", children: [
5646
- loading ? /* @__PURE__ */ jsx("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ jsx(IconLink, { size: 13 }),
5647
- /* @__PURE__ */ jsx(
4896
+ /* @__PURE__ */ jsxs7("div", { className: "rk-url", children: [
4897
+ loading2 ? /* @__PURE__ */ jsx8("span", { className: "rk-spinner", "aria-label": "Loading image" }) : /* @__PURE__ */ jsx8(IconLink, { size: 13 }),
4898
+ /* @__PURE__ */ jsx8(
5648
4899
  "input",
5649
4900
  {
5650
4901
  className: "rk-input",
@@ -5659,9 +4910,9 @@ function ImageEditor({ editor }) {
5659
4910
  )
5660
4911
  ] })
5661
4912
  ] }),
5662
- replacement && /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
5663
- /* @__PURE__ */ jsx("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
5664
- /* @__PURE__ */ jsx(
4913
+ replacement && /* @__PURE__ */ jsx8("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: clearReplacement, children: "Remove replacement" }),
4914
+ /* @__PURE__ */ jsx8("input", { ref: fileInput, type: "file", accept: "image/*", hidden: true, onChange: onPick }),
4915
+ /* @__PURE__ */ jsx8(
5665
4916
  "textarea",
5666
4917
  {
5667
4918
  className: "rk-textarea",
@@ -5671,39 +4922,43 @@ function ImageEditor({ editor }) {
5671
4922
  rows: 2
5672
4923
  }
5673
4924
  ),
5674
- /* @__PURE__ */ jsxs("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5675
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5676
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
4925
+ /* @__PURE__ */ jsxs7("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
4926
+ /* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
4927
+ /* @__PURE__ */ jsx8("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5677
4928
  ] })
5678
4929
  ] }),
5679
- /* @__PURE__ */ jsxs("div", { className: "rk-card-foot", children: [
5680
- /* @__PURE__ */ jsxs("span", { className: "rk-hint", children: [
5681
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "esc" }),
4930
+ /* @__PURE__ */ jsxs7("div", { className: "rk-card-foot", children: [
4931
+ /* @__PURE__ */ jsxs7("span", { className: "rk-hint", children: [
4932
+ /* @__PURE__ */ jsx8("span", { className: "rk-kbd", children: "esc" }),
5682
4933
  " cancel"
5683
4934
  ] }),
5684
- /* @__PURE__ */ jsxs("div", { className: "rk-actions", children: [
5685
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5686
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
4935
+ /* @__PURE__ */ jsxs7("div", { className: "rk-actions", children: [
4936
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
4937
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
5687
4938
  ] })
5688
4939
  ] })
5689
4940
  ] });
5690
4941
  }
4942
+
4943
+ // src/client/components/InlineEditor.tsx
4944
+ import { useEffect as useEffect7, useRef as useRef8, useState as useState6 } from "react";
4945
+ import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
5691
4946
  function InlineEditor({ editor }) {
5692
4947
  const dispatch = useDispatch();
5693
4948
  const { route, session } = useStore();
5694
- const [after, setAfter] = useState(() => editor.element.innerText);
5695
- const [priority, setPriority] = useState("nice");
5696
- const [note, setNote] = useState("");
5697
- const original = useRef(null);
5698
- const baseline = useRef(null);
5699
- const [sizeDelta, setSizeDelta] = useState(0);
5700
- const [widthDelta, setWidthDelta] = useState(0);
5701
- const [clipped, setClipped] = useState(false);
5702
- const ringRef = useRef(null);
5703
- const afterRef = useRef(null);
4949
+ const [after, setAfter] = useState6(() => editor.element.innerText);
4950
+ const [priority, setPriority] = useState6("nice");
4951
+ const [note, setNote] = useState6("");
4952
+ const original = useRef8(null);
4953
+ const baseline = useRef8(null);
4954
+ const [sizeDelta, setSizeDelta] = useState6(0);
4955
+ const [widthDelta, setWidthDelta] = useState6(0);
4956
+ const [clipped, setClipped] = useState6(false);
4957
+ const ringRef = useRef8(null);
4958
+ const afterRef = useRef8(null);
5704
4959
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5705
4960
  const element = editor.element;
5706
- useEffect(() => {
4961
+ useEffect7(() => {
5707
4962
  const el = element;
5708
4963
  original.current = { html: el.innerHTML, text: el.innerText, styleAttr: el.getAttribute("style") };
5709
4964
  const rect = el.getBoundingClientRect();
@@ -5733,8 +4988,8 @@ function InlineEditor({ editor }) {
5733
4988
  }
5734
4989
  };
5735
4990
  }, [element]);
5736
- const focusedOnce = useRef(false);
5737
- useEffect(() => {
4991
+ const focusedOnce = useRef8(false);
4992
+ useEffect7(() => {
5738
4993
  const field = afterRef.current;
5739
4994
  if (pos === null || focusedOnce.current || !field) return;
5740
4995
  focusedOnce.current = true;
@@ -5742,7 +4997,7 @@ function InlineEditor({ editor }) {
5742
4997
  const end = field.value.length;
5743
4998
  field.setSelectionRange(end, end);
5744
4999
  }, [pos]);
5745
- useEffect(() => {
5000
+ useEffect7(() => {
5746
5001
  const field = afterRef.current;
5747
5002
  if (!field) return;
5748
5003
  field.style.height = "auto";
@@ -5752,7 +5007,7 @@ function InlineEditor({ editor }) {
5752
5007
  setAfter(value);
5753
5008
  if (element.innerText !== value) setElementText(element, value);
5754
5009
  };
5755
- useEffect(() => {
5010
+ useEffect7(() => {
5756
5011
  let raf = 0;
5757
5012
  let lastDelta = 0;
5758
5013
  let lastWidth = 0;
@@ -5822,7 +5077,7 @@ function InlineEditor({ editor }) {
5822
5077
  cancel();
5823
5078
  }
5824
5079
  };
5825
- useEffect(() => {
5080
+ useEffect7(() => {
5826
5081
  const el = element;
5827
5082
  const onKeyDown = (event) => {
5828
5083
  if (event.key === "Enter" && !event.shiftKey) {
@@ -5840,19 +5095,19 @@ function InlineEditor({ editor }) {
5840
5095
  });
5841
5096
  const before = original.current?.text ?? "";
5842
5097
  const delta = after.length - before.length;
5843
- return /* @__PURE__ */ jsxs(Fragment, { children: [
5844
- /* @__PURE__ */ jsx("div", { ref: ringRef, className: "rk-edit-ring" }),
5845
- /* @__PURE__ */ jsxs("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
5846
- /* @__PURE__ */ jsxs("div", { className: "rk-card-head", children: [
5847
- /* @__PURE__ */ jsx(IconText, { size: 14 }),
5098
+ return /* @__PURE__ */ jsxs8(Fragment5, { children: [
5099
+ /* @__PURE__ */ jsx9("div", { ref: ringRef, className: "rk-edit-ring" }),
5100
+ /* @__PURE__ */ jsxs8("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": "Rewrite text", children: [
5101
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-head", children: [
5102
+ /* @__PURE__ */ jsx9(IconText, { size: 14 }),
5848
5103
  "Rewrite text",
5849
- /* @__PURE__ */ jsx("span", { className: "rk-spacer" }),
5850
- /* @__PURE__ */ jsxs("span", { className: "rk-delta", "aria-live": "polite", children: [
5104
+ /* @__PURE__ */ jsx9("span", { className: "rk-spacer" }),
5105
+ /* @__PURE__ */ jsxs8("span", { className: "rk-delta", "aria-live": "polite", children: [
5851
5106
  before.length,
5852
5107
  " \u2192 ",
5853
5108
  after.length,
5854
5109
  " ",
5855
- delta !== 0 && /* @__PURE__ */ jsxs("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
5110
+ delta !== 0 && /* @__PURE__ */ jsxs8("span", { className: delta > 0 ? "rk-plus" : "rk-minus", children: [
5856
5111
  "(",
5857
5112
  delta > 0 ? "+" : "",
5858
5113
  delta,
@@ -5860,15 +5115,15 @@ function InlineEditor({ editor }) {
5860
5115
  ] })
5861
5116
  ] })
5862
5117
  ] }),
5863
- /* @__PURE__ */ jsxs("div", { className: "rk-card-body", children: [
5864
- /* @__PURE__ */ jsxs("div", { className: "rk-diff", "aria-label": "Before and after", children: [
5865
- /* @__PURE__ */ jsxs("div", { className: "rk-diff-row rk-diff-before", children: [
5866
- /* @__PURE__ */ jsx("span", { className: "rk-diff-tag", children: "Before" }),
5867
- /* @__PURE__ */ jsx("span", { className: "rk-diff-text", children: before })
5118
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-body", children: [
5119
+ /* @__PURE__ */ jsxs8("div", { className: "rk-diff", "aria-label": "Before and after", children: [
5120
+ /* @__PURE__ */ jsxs8("div", { className: "rk-diff-row rk-diff-before", children: [
5121
+ /* @__PURE__ */ jsx9("span", { className: "rk-diff-tag", children: "Before" }),
5122
+ /* @__PURE__ */ jsx9("span", { className: "rk-diff-text", children: before })
5868
5123
  ] }),
5869
- /* @__PURE__ */ jsxs("div", { className: "rk-diff-row rk-diff-after", children: [
5870
- /* @__PURE__ */ jsx("span", { className: "rk-diff-tag", children: "After" }),
5871
- /* @__PURE__ */ jsx(
5124
+ /* @__PURE__ */ jsxs8("div", { className: "rk-diff-row rk-diff-after", children: [
5125
+ /* @__PURE__ */ jsx9("span", { className: "rk-diff-tag", children: "After" }),
5126
+ /* @__PURE__ */ jsx9(
5872
5127
  "textarea",
5873
5128
  {
5874
5129
  ref: afterRef,
@@ -5882,11 +5137,11 @@ function InlineEditor({ editor }) {
5882
5137
  )
5883
5138
  ] })
5884
5139
  ] }),
5885
- (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ jsxs("div", { className: "rk-size-note", role: "status", children: [
5140
+ (Math.abs(sizeDelta) > 2 || Math.abs(widthDelta) > 4 || clipped) && /* @__PURE__ */ jsxs8("div", { className: "rk-size-note", role: "status", children: [
5886
5141
  Math.abs(sizeDelta) > 2 && (() => {
5887
5142
  const lines = baseline.current ? Math.round(Math.abs(sizeDelta) / baseline.current.lineHeight) : 0;
5888
5143
  const lineNote = lines >= 1 ? ` (~${lines} line${lines === 1 ? "" : "s"})` : "";
5889
- return /* @__PURE__ */ jsxs("span", { children: [
5144
+ return /* @__PURE__ */ jsxs8("span", { children: [
5890
5145
  "Block ",
5891
5146
  sizeDelta > 0 ? "grew" : "shrank",
5892
5147
  " by ",
@@ -5896,16 +5151,16 @@ function InlineEditor({ editor }) {
5896
5151
  " \u2014 as it would with this text live."
5897
5152
  ] });
5898
5153
  })(),
5899
- Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ jsxs("span", { children: [
5154
+ Math.abs(sizeDelta) <= 2 && Math.abs(widthDelta) > 4 && /* @__PURE__ */ jsxs8("span", { children: [
5900
5155
  "Block got ",
5901
5156
  widthDelta > 0 ? "wider" : "narrower",
5902
5157
  " by ",
5903
5158
  Math.abs(widthDelta),
5904
5159
  " px \u2014 as it would with this text live."
5905
5160
  ] }),
5906
- clipped && /* @__PURE__ */ jsx("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
5161
+ clipped && /* @__PURE__ */ jsx9("span", { className: "rk-clip-note", children: "Text exceeds the visible crop here and will be cut off." })
5907
5162
  ] }),
5908
- /* @__PURE__ */ jsx(
5163
+ /* @__PURE__ */ jsx9(
5909
5164
  "input",
5910
5165
  {
5911
5166
  className: "rk-input",
@@ -5914,29 +5169,33 @@ function InlineEditor({ editor }) {
5914
5169
  onChange: (event) => setNote(event.target.value)
5915
5170
  }
5916
5171
  ),
5917
- /* @__PURE__ */ jsxs("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5918
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5919
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5172
+ /* @__PURE__ */ jsxs8("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5173
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5174
+ /* @__PURE__ */ jsx9("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5920
5175
  ] })
5921
5176
  ] }),
5922
- /* @__PURE__ */ jsxs("div", { className: "rk-card-foot", children: [
5923
- /* @__PURE__ */ jsxs("span", { className: "rk-hint", children: [
5924
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "\u21B5" }),
5177
+ /* @__PURE__ */ jsxs8("div", { className: "rk-card-foot", children: [
5178
+ /* @__PURE__ */ jsxs8("span", { className: "rk-hint", children: [
5179
+ /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "\u21B5" }),
5925
5180
  " save \xB7 ",
5926
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
5181
+ /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "\u21E7\u21B5" }),
5927
5182
  " newline \xB7",
5928
5183
  " ",
5929
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "esc" }),
5184
+ /* @__PURE__ */ jsx9("span", { className: "rk-kbd", children: "esc" }),
5930
5185
  " cancel"
5931
5186
  ] }),
5932
- /* @__PURE__ */ jsxs("div", { className: "rk-actions", children: [
5933
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
5934
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
5187
+ /* @__PURE__ */ jsxs8("div", { className: "rk-actions", children: [
5188
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: cancel, children: "Cancel" }),
5189
+ /* @__PURE__ */ jsx9("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: commit, children: "Save" })
5935
5190
  ] })
5936
5191
  ] })
5937
5192
  ] })
5938
5193
  ] });
5939
5194
  }
5195
+
5196
+ // src/client/components/NoteComposer.tsx
5197
+ import { useEffect as useEffect8, useRef as useRef9, useState as useState7 } from "react";
5198
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
5940
5199
  var TITLES = {
5941
5200
  comment: "Comment",
5942
5201
  delete: "Mark for deletion",
@@ -5952,14 +5211,14 @@ var PLACEHOLDERS = {
5952
5211
  function NoteComposer({ editor }) {
5953
5212
  const dispatch = useDispatch();
5954
5213
  const { route, session } = useStore();
5955
- const [text, setText] = useState("");
5956
- const [direction, setDirection] = useState(void 0);
5957
- const [priority, setPriority] = useState("nice");
5958
- const textareaRef = useRef(null);
5214
+ const [text, setText] = useState7("");
5215
+ const [direction, setDirection] = useState7(void 0);
5216
+ const [priority, setPriority] = useState7("nice");
5217
+ const textareaRef = useRef9(null);
5959
5218
  const { ref, pos } = useAnchored(editor.element, "bottom", 12);
5960
5219
  const { kind } = editor;
5961
- const focusedOnce = useRef(false);
5962
- useEffect(() => {
5220
+ const focusedOnce = useRef9(false);
5221
+ useEffect8(() => {
5963
5222
  const field = textareaRef.current;
5964
5223
  if (pos === null || focusedOnce.current || !field) return;
5965
5224
  focusedOnce.current = true;
@@ -6018,13 +5277,13 @@ function NoteComposer({ editor }) {
6018
5277
  save();
6019
5278
  }
6020
5279
  };
6021
- return /* @__PURE__ */ jsxs("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
6022
- /* @__PURE__ */ jsxs("div", { className: "rk-card-head", children: [
5280
+ return /* @__PURE__ */ jsxs9("div", { ref, className: "rk-card", style: anchoredStyle(pos), role: "dialog", "aria-label": TITLES[kind], children: [
5281
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-head", children: [
6023
5282
  iconForType(kind),
6024
5283
  TITLES[kind]
6025
5284
  ] }),
6026
- /* @__PURE__ */ jsxs("div", { className: "rk-card-body", children: [
6027
- kind === "move" && /* @__PURE__ */ jsx("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ jsx(
5285
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-body", children: [
5286
+ kind === "move" && /* @__PURE__ */ jsx10("div", { className: "rk-seg", role: "group", "aria-label": "Direction", children: ["up", "down", "first", "last"].map((option) => /* @__PURE__ */ jsx10(
6028
5287
  "button",
6029
5288
  {
6030
5289
  type: "button",
@@ -6034,7 +5293,7 @@ function NoteComposer({ editor }) {
6034
5293
  },
6035
5294
  option
6036
5295
  )) }),
6037
- /* @__PURE__ */ jsx(
5296
+ /* @__PURE__ */ jsx10(
6038
5297
  "textarea",
6039
5298
  {
6040
5299
  ref: textareaRef,
@@ -6046,25 +5305,29 @@ function NoteComposer({ editor }) {
6046
5305
  rows: 3
6047
5306
  }
6048
5307
  ),
6049
- /* @__PURE__ */ jsxs("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6050
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
6051
- /* @__PURE__ */ jsx("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
5308
+ /* @__PURE__ */ jsxs9("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
5309
+ /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "must", onClick: () => setPriority("must"), children: "Must" }),
5310
+ /* @__PURE__ */ jsx10("button", { type: "button", "aria-pressed": priority === "nice", onClick: () => setPriority("nice"), children: "Nice to have" })
6052
5311
  ] })
6053
5312
  ] }),
6054
- /* @__PURE__ */ jsxs("div", { className: "rk-card-foot", children: [
6055
- /* @__PURE__ */ jsxs("span", { className: "rk-hint", children: [
6056
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
5313
+ /* @__PURE__ */ jsxs9("div", { className: "rk-card-foot", children: [
5314
+ /* @__PURE__ */ jsxs9("span", { className: "rk-hint", children: [
5315
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "\u2318\u21B5" }),
6057
5316
  " save \xB7 ",
6058
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "esc" }),
5317
+ /* @__PURE__ */ jsx10("span", { className: "rk-kbd", children: "esc" }),
6059
5318
  " cancel"
6060
5319
  ] }),
6061
- /* @__PURE__ */ jsxs("div", { className: "rk-actions", children: [
6062
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
6063
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
5320
+ /* @__PURE__ */ jsxs9("div", { className: "rk-actions", children: [
5321
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => dispatch({ type: "CLOSE_EDITOR" }), children: "Cancel" }),
5322
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "rk-btn rk-btn-primary", onClick: save, children: "Save" })
6064
5323
  ] })
6065
5324
  ] })
6066
5325
  ] });
6067
5326
  }
5327
+
5328
+ // src/client/components/Panel.tsx
5329
+ import { useEffect as useEffect9, useMemo as useMemo3, useRef as useRef10, useState as useState8 } from "react";
5330
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
6068
5331
  function changeSummary(item) {
6069
5332
  if (isCopyItem(item)) return `\u201C${item.payload.after}\u201D`;
6070
5333
  if (isImageItem(item)) {
@@ -6146,24 +5409,24 @@ function formatBytes(bytes) {
6146
5409
  function CopyDiff({ before, after }) {
6147
5410
  const parts = diffParts(before, after);
6148
5411
  const delta = after.length - before.length;
6149
- return /* @__PURE__ */ jsxs("div", { className: "rk-diffview", children: [
6150
- /* @__PURE__ */ jsxs("div", { className: "rk-diff-row rk-diff-before", children: [
6151
- /* @__PURE__ */ jsx("span", { className: "rk-diff-tag", children: "Before" }),
6152
- /* @__PURE__ */ jsxs("span", { className: "rk-diff-text", children: [
5412
+ return /* @__PURE__ */ jsxs10("div", { className: "rk-diffview", children: [
5413
+ /* @__PURE__ */ jsxs10("div", { className: "rk-diff-row rk-diff-before", children: [
5414
+ /* @__PURE__ */ jsx11("span", { className: "rk-diff-tag", children: "Before" }),
5415
+ /* @__PURE__ */ jsxs10("span", { className: "rk-diff-text", children: [
6153
5416
  parts.prefix,
6154
- parts.delMid && /* @__PURE__ */ jsx("mark", { className: "rk-del", children: parts.delMid }),
5417
+ parts.delMid && /* @__PURE__ */ jsx11("mark", { className: "rk-del", children: parts.delMid }),
6155
5418
  parts.suffix
6156
5419
  ] })
6157
5420
  ] }),
6158
- /* @__PURE__ */ jsxs("div", { className: "rk-diff-row rk-diff-after", children: [
6159
- /* @__PURE__ */ jsx("span", { className: "rk-diff-tag", children: "After" }),
6160
- /* @__PURE__ */ jsxs("span", { className: "rk-diff-text", children: [
5421
+ /* @__PURE__ */ jsxs10("div", { className: "rk-diff-row rk-diff-after", children: [
5422
+ /* @__PURE__ */ jsx11("span", { className: "rk-diff-tag", children: "After" }),
5423
+ /* @__PURE__ */ jsxs10("span", { className: "rk-diff-text", children: [
6161
5424
  parts.prefix,
6162
- parts.insMid && /* @__PURE__ */ jsx("mark", { className: "rk-ins", children: parts.insMid }),
5425
+ parts.insMid && /* @__PURE__ */ jsx11("mark", { className: "rk-ins", children: parts.insMid }),
6163
5426
  parts.suffix
6164
5427
  ] })
6165
5428
  ] }),
6166
- /* @__PURE__ */ jsxs("div", { className: "rk-diff-len", children: [
5429
+ /* @__PURE__ */ jsxs10("div", { className: "rk-diff-len", children: [
6167
5430
  before.length,
6168
5431
  " \u2192 ",
6169
5432
  after.length,
@@ -6174,8 +5437,8 @@ function CopyDiff({ before, after }) {
6174
5437
  }
6175
5438
  function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6176
5439
  const replacement = afterDataUrl ?? afterSrc;
6177
- const [dims, setDims] = useState("");
6178
- useEffect(() => {
5440
+ const [dims, setDims] = useState8("");
5441
+ useEffect9(() => {
6179
5442
  if (!replacement) return;
6180
5443
  const img = new Image();
6181
5444
  img.onload = () => setDims(`${img.naturalWidth}\xD7${img.naturalHeight}px`);
@@ -6200,32 +5463,32 @@ function ImageDetail({ beforeSrc, afterSrc, afterDataUrl }) {
6200
5463
  window.open(replacement, "_blank", "noopener");
6201
5464
  }
6202
5465
  };
6203
- return /* @__PURE__ */ jsxs("div", { className: "rk-imgdetail", children: [
6204
- /* @__PURE__ */ jsxs("div", { className: "rk-thumbs", children: [
6205
- /* @__PURE__ */ jsxs("figure", { className: "rk-thumb", children: [
6206
- beforeSrc ? /* @__PURE__ */ jsx("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ jsx("div", { className: "rk-thumb-missing", children: "no image" }),
6207
- /* @__PURE__ */ jsx("figcaption", { children: "Before" })
5466
+ return /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail", children: [
5467
+ /* @__PURE__ */ jsxs10("div", { className: "rk-thumbs", children: [
5468
+ /* @__PURE__ */ jsxs10("figure", { className: "rk-thumb", children: [
5469
+ beforeSrc ? /* @__PURE__ */ jsx11("img", { src: beforeSrc, alt: "Current" }) : /* @__PURE__ */ jsx11("div", { className: "rk-thumb-missing", children: "no image" }),
5470
+ /* @__PURE__ */ jsx11("figcaption", { children: "Before" })
6208
5471
  ] }),
6209
- /* @__PURE__ */ jsxs("figure", { className: "rk-thumb", children: [
6210
- /* @__PURE__ */ jsx("img", { src: replacement, alt: "Proposed replacement" }),
6211
- /* @__PURE__ */ jsxs("figcaption", { children: [
5472
+ /* @__PURE__ */ jsxs10("figure", { className: "rk-thumb", children: [
5473
+ /* @__PURE__ */ jsx11("img", { src: replacement, alt: "Proposed replacement" }),
5474
+ /* @__PURE__ */ jsxs10("figcaption", { children: [
6212
5475
  "After",
6213
5476
  embedded ? "" : " \xB7 linked"
6214
5477
  ] })
6215
5478
  ] })
6216
5479
  ] }),
6217
- /* @__PURE__ */ jsxs("div", { className: "rk-imgdetail-meta", children: [
5480
+ /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail-meta", children: [
6218
5481
  embedded ? "Embedded image" : "Linked image",
6219
5482
  dims ? ` \xB7 ${dims}` : "",
6220
5483
  bytes !== null ? ` \xB7 ${formatBytes(bytes)}` : ""
6221
5484
  ] }),
6222
- !embedded && afterSrc && /* @__PURE__ */ jsx("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
6223
- /* @__PURE__ */ jsxs("div", { className: "rk-imgdetail-actions", children: [
6224
- /* @__PURE__ */ jsxs("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
6225
- /* @__PURE__ */ jsx(IconDownload, { size: 12 }),
5485
+ !embedded && afterSrc && /* @__PURE__ */ jsx11("a", { className: "rk-imgdetail-url", href: afterSrc, target: "_blank", rel: "noopener noreferrer", title: afterSrc, children: afterSrc }),
5486
+ /* @__PURE__ */ jsxs10("div", { className: "rk-imgdetail-actions", children: [
5487
+ /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void download(), children: [
5488
+ /* @__PURE__ */ jsx11(IconDownload, { size: 12 }),
6226
5489
  " Download"
6227
5490
  ] }),
6228
- /* @__PURE__ */ jsx("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
5491
+ /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => window.open(replacement, "_blank", "noopener"), children: "Open" })
6229
5492
  ] })
6230
5493
  ] });
6231
5494
  }
@@ -6233,22 +5496,22 @@ function Panel() {
6233
5496
  const { session, panelOpen, activeItemId, badgeCorner, undoStack, preview, unapplied, syncStatus, emblemaPush, peers } = useStore();
6234
5497
  const dispatch = useDispatch();
6235
5498
  const config = useConfig();
6236
- const [sticky, setStickyState] = useState(() => isStickyEnabled(config.token));
6237
- const [query, setQuery] = useState("");
6238
- const [typeFilter, setTypeFilter] = useState(/* @__PURE__ */ new Set());
6239
- const [prioFilter, setPrioFilter] = useState(/* @__PURE__ */ new Set());
6240
- const [statusFilter, setStatusFilter] = useState(/* @__PURE__ */ new Set());
6241
- const [filterOpen, setFilterOpen] = useState(false);
6242
- const [settingsOpen, setSettingsOpen] = useState(false);
6243
- const [emblemaAlive, setEmblemaAlive] = useState(null);
6244
- const [emblemaSending, setEmblemaSending] = useState(false);
6245
- const [emblemaSync, setEmblemaSync] = useState("unknown");
6246
- const panelRef = useRef(null);
6247
- const searchRef = useRef(null);
6248
- const importRef = useRef(null);
6249
- const sheetDrag = useRef(null);
6250
- const items = useMemo(() => session?.items ?? [], [session]);
6251
- const { orphanIds, hiddenIds } = useMemo(() => {
5499
+ const [sticky, setStickyState] = useState8(() => isStickyEnabled(config.token));
5500
+ const [query, setQuery] = useState8("");
5501
+ const [typeFilter, setTypeFilter] = useState8(/* @__PURE__ */ new Set());
5502
+ const [prioFilter, setPrioFilter] = useState8(/* @__PURE__ */ new Set());
5503
+ const [statusFilter, setStatusFilter] = useState8(/* @__PURE__ */ new Set());
5504
+ const [filterOpen, setFilterOpen] = useState8(false);
5505
+ const [settingsOpen, setSettingsOpen] = useState8(false);
5506
+ const [emblemaAlive, setEmblemaAlive] = useState8(null);
5507
+ const [emblemaSending, setEmblemaSending] = useState8(false);
5508
+ const [emblemaSync, setEmblemaSync] = useState8("unknown");
5509
+ const panelRef = useRef10(null);
5510
+ const searchRef = useRef10(null);
5511
+ const importRef = useRef10(null);
5512
+ const sheetDrag = useRef10(null);
5513
+ const items = useMemo3(() => session?.items ?? [], [session]);
5514
+ const { orphanIds, hiddenIds } = useMemo3(() => {
6252
5515
  const orphans = /* @__PURE__ */ new Set();
6253
5516
  const hiddens = /* @__PURE__ */ new Set();
6254
5517
  for (const item of items) {
@@ -6259,15 +5522,15 @@ function Panel() {
6259
5522
  }
6260
5523
  return { orphanIds: orphans, hiddenIds: hiddens };
6261
5524
  }, [items, panelOpen]);
6262
- const activeFilter = useMemo(
5525
+ const activeFilter = useMemo3(
6263
5526
  () => typeFilter.size + prioFilter.size + statusFilter.size > 0 ? { types: [...typeFilter], priorities: [...prioFilter], statuses: [...statusFilter] } : null,
6264
5527
  [typeFilter, prioFilter, statusFilter]
6265
5528
  );
6266
- useEffect(() => {
5529
+ useEffect9(() => {
6267
5530
  dispatch({ type: "SET_FILTER", filter: activeFilter });
6268
5531
  return () => dispatch({ type: "SET_FILTER", filter: null });
6269
5532
  }, [activeFilter, dispatch]);
6270
- const filtered = useMemo(() => {
5533
+ const filtered = useMemo3(() => {
6271
5534
  const q = query.trim().toLowerCase();
6272
5535
  return items.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => {
6273
5536
  if (!itemMatchesFilter(item, activeFilter)) return false;
@@ -6276,22 +5539,22 @@ function Panel() {
6276
5539
  return haystack.includes(q);
6277
5540
  });
6278
5541
  }, [items, query, activeFilter]);
6279
- const typeCounts = useMemo(() => {
5542
+ const typeCounts = useMemo3(() => {
6280
5543
  const counts = /* @__PURE__ */ new Map();
6281
5544
  for (const item of items) counts.set(item.type, (counts.get(item.type) ?? 0) + 1);
6282
5545
  return counts;
6283
5546
  }, [items]);
6284
- const prioCounts = useMemo(() => {
5547
+ const prioCounts = useMemo3(() => {
6285
5548
  const counts = /* @__PURE__ */ new Map();
6286
5549
  for (const item of items) counts.set(item.priority, (counts.get(item.priority) ?? 0) + 1);
6287
5550
  return counts;
6288
5551
  }, [items]);
6289
- const statusCounts = useMemo(() => {
5552
+ const statusCounts = useMemo3(() => {
6290
5553
  const counts = /* @__PURE__ */ new Map();
6291
5554
  for (const item of items) counts.set(item.status, (counts.get(item.status) ?? 0) + 1);
6292
5555
  return counts;
6293
5556
  }, [items]);
6294
- const groups = useMemo(() => {
5557
+ const groups = useMemo3(() => {
6295
5558
  const map = /* @__PURE__ */ new Map();
6296
5559
  for (const entry of filtered) {
6297
5560
  const list = map.get(entry.item.route) ?? [];
@@ -6300,7 +5563,7 @@ function Panel() {
6300
5563
  }
6301
5564
  return Array.from(map.entries());
6302
5565
  }, [filtered]);
6303
- useEffect(() => {
5566
+ useEffect9(() => {
6304
5567
  if (!filterOpen && !settingsOpen) return;
6305
5568
  const onDown = (event) => {
6306
5569
  const path = event.composedPath();
@@ -6311,7 +5574,7 @@ function Panel() {
6311
5574
  document.addEventListener("pointerdown", onDown, true);
6312
5575
  return () => document.removeEventListener("pointerdown", onDown, true);
6313
5576
  }, [filterOpen, settingsOpen]);
6314
- useEffect(() => {
5577
+ useEffect9(() => {
6315
5578
  if (!settingsOpen) return;
6316
5579
  let alive = true;
6317
5580
  const probe = () => {
@@ -6335,7 +5598,7 @@ function Panel() {
6335
5598
  live: "paired",
6336
5599
  off: "not paired"
6337
5600
  }[emblemaPairState];
6338
- useEffect(() => {
5601
+ useEffect9(() => {
6339
5602
  if (!panelOpen || activeItemId === null) return;
6340
5603
  const row = panelRef.current?.querySelector(`[data-item-id="${activeItemId}"]`);
6341
5604
  row?.scrollIntoView({ block: "nearest" });
@@ -6366,11 +5629,11 @@ function Panel() {
6366
5629
  if (focusable.length === 0) return;
6367
5630
  const first = focusable[0];
6368
5631
  const last = focusable[focusable.length - 1];
6369
- const active2 = panel.getRootNode() instanceof ShadowRoot ? panel.getRootNode().activeElement : document.activeElement;
6370
- if (event.shiftKey && active2 === first) {
5632
+ const active = panel.getRootNode() instanceof ShadowRoot ? panel.getRootNode().activeElement : document.activeElement;
5633
+ if (event.shiftKey && active === first) {
6371
5634
  event.preventDefault();
6372
5635
  last.focus();
6373
- } else if (!event.shiftKey && active2 === last) {
5636
+ } else if (!event.shiftKey && active === last) {
6374
5637
  event.preventDefault();
6375
5638
  first.focus();
6376
5639
  }
@@ -6451,7 +5714,7 @@ function Panel() {
6451
5714
  panel.style.transform = "";
6452
5715
  if (dy > 110) dispatch({ type: "SET_PANEL", open: false });
6453
5716
  };
6454
- return /* @__PURE__ */ jsxs(
5717
+ return /* @__PURE__ */ jsxs10(
6455
5718
  "div",
6456
5719
  {
6457
5720
  ref: panelRef,
@@ -6461,7 +5724,7 @@ function Panel() {
6461
5724
  "aria-label": "Review feedback panel",
6462
5725
  onKeyDown,
6463
5726
  children: [
6464
- /* @__PURE__ */ jsx(
5727
+ /* @__PURE__ */ jsx11(
6465
5728
  "div",
6466
5729
  {
6467
5730
  className: "rk-sheet-handle",
@@ -6472,34 +5735,34 @@ function Panel() {
6472
5735
  onPointerCancel: onSheetHandleUp
6473
5736
  }
6474
5737
  ),
6475
- /* @__PURE__ */ jsxs("div", { className: "rk-panel-head", children: [
6476
- /* @__PURE__ */ jsxs("div", { children: [
6477
- /* @__PURE__ */ jsx("div", { className: "rk-panel-title", children: "Feedback" }),
6478
- /* @__PURE__ */ jsxs("div", { className: "rk-panel-sub", children: [
5738
+ /* @__PURE__ */ jsxs10("div", { className: "rk-panel-head", children: [
5739
+ /* @__PURE__ */ jsxs10("div", { children: [
5740
+ /* @__PURE__ */ jsx11("div", { className: "rk-panel-title", children: "Feedback" }),
5741
+ /* @__PURE__ */ jsxs10("div", { className: "rk-panel-sub", children: [
6479
5742
  items.length,
6480
5743
  " item",
6481
5744
  items.length === 1 ? "" : "s",
6482
5745
  session.author ? ` \xB7 ${session.author}` : "",
6483
- syncStatus !== "disabled" && /* @__PURE__ */ jsxs("span", { className: `rk-sync rk-sync-${syncStatus}`, children: [
6484
- /* @__PURE__ */ jsx("span", { className: "rk-sync-dot" }),
5746
+ syncStatus !== "disabled" && /* @__PURE__ */ jsxs10("span", { className: `rk-sync rk-sync-${syncStatus}`, children: [
5747
+ /* @__PURE__ */ jsx11("span", { className: "rk-sync-dot" }),
6485
5748
  syncStatus === "online" ? `live${peers.length > 0 ? ` \xB7 ${peers.length} other${peers.length === 1 ? "" : "s"}` : ""}` : syncStatus === "connecting" ? "connecting\u2026" : "local only"
6486
5749
  ] }),
6487
- config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ jsxs(
5750
+ config.emblema && emblemaPush !== "idle" && /* @__PURE__ */ jsxs10(
6488
5751
  "span",
6489
5752
  {
6490
5753
  className: `rk-sync rk-epush-${emblemaPush}`,
6491
5754
  title: emblemaPush === "ok" ? "Review items stream to Emblema automatically" : emblemaPush === "sending" ? "Sending items to Emblema\u2026" : "Emblema isn't reachable \u2014 items resend on the next change, or use Resend all in the menu",
6492
5755
  children: [
6493
- /* @__PURE__ */ jsx("span", { className: "rk-sync-dot" }),
6494
- emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : "emblema offline"
5756
+ /* @__PURE__ */ jsx11("span", { className: "rk-sync-dot" }),
5757
+ emblemaPush === "ok" ? "emblema" : emblemaPush === "sending" ? "emblema\u2026" : emblemaPush === "rejected" ? "emblema rejected" : "emblema offline"
6495
5758
  ]
6496
5759
  }
6497
5760
  )
6498
5761
  ] })
6499
5762
  ] }),
6500
- /* @__PURE__ */ jsx("span", { className: "rk-spacer" }),
6501
- /* @__PURE__ */ jsxs("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6502
- /* @__PURE__ */ jsx(
5763
+ /* @__PURE__ */ jsx11("span", { className: "rk-spacer" }),
5764
+ /* @__PURE__ */ jsxs10("div", { className: "rk-menu-scope rk-menu-anchor", children: [
5765
+ /* @__PURE__ */ jsx11(
6503
5766
  "button",
6504
5767
  {
6505
5768
  type: "button",
@@ -6515,11 +5778,11 @@ function Panel() {
6515
5778
  void pingEmblema(config.emblema).then((port) => setEmblemaAlive(port !== null));
6516
5779
  }
6517
5780
  },
6518
- children: /* @__PURE__ */ jsx(IconGear, { size: 15 })
5781
+ children: /* @__PURE__ */ jsx11(IconGear, { size: 15 })
6519
5782
  }
6520
5783
  ),
6521
- settingsOpen && /* @__PURE__ */ jsxs("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
6522
- /* @__PURE__ */ jsxs(
5784
+ settingsOpen && /* @__PURE__ */ jsxs10("div", { className: "rk-menu", role: "menu", "aria-label": "Session settings", children: [
5785
+ config.token && /* @__PURE__ */ jsxs10(
6523
5786
  "button",
6524
5787
  {
6525
5788
  type: "button",
@@ -6534,12 +5797,12 @@ function Panel() {
6534
5797
  toast(dispatch, next ? "Review stays on in this tab" : "Review active only via the link", "info");
6535
5798
  },
6536
5799
  children: [
6537
- /* @__PURE__ */ jsx("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
5800
+ /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${sticky ? " rk-on" : ""}` }),
6538
5801
  "Keep review on in this tab"
6539
5802
  ]
6540
5803
  }
6541
5804
  ),
6542
- config.emblema && /* @__PURE__ */ jsxs(
5805
+ config.emblema && /* @__PURE__ */ jsxs10(
6543
5806
  "button",
6544
5807
  {
6545
5808
  type: "button",
@@ -6569,7 +5832,7 @@ function Panel() {
6569
5832
  }).finally(() => setEmblemaSending(false));
6570
5833
  },
6571
5834
  children: [
6572
- /* @__PURE__ */ jsx(
5835
+ /* @__PURE__ */ jsx11(
6573
5836
  "span",
6574
5837
  {
6575
5838
  className: "rk-emblema-dot",
@@ -6580,25 +5843,25 @@ function Panel() {
6580
5843
  ]
6581
5844
  }
6582
5845
  ),
6583
- /* @__PURE__ */ jsxs(
5846
+ /* @__PURE__ */ jsxs10(
6584
5847
  "button",
6585
5848
  {
6586
5849
  type: "button",
6587
5850
  role: "menuitem",
6588
5851
  className: "rk-menu-item",
6589
- title: emblemaPairState === "stale" ? "The local sync agent is not answering \u2014 run `npx reviewkit-emblema serve` in the project" : "Pair this project with the Emblema app via a sync key",
5852
+ title: emblemaPairState === "stale" ? "The local sync agent is not answering \u2014 run `npx @satanwagen/reviewkit-cli serve` in the project" : "Pair this project with the Emblema app via a sync key",
6590
5853
  onClick: () => {
6591
5854
  setSettingsOpen(false);
6592
5855
  dispatch({ type: "SET_EMBLEMA_CONNECT", open: true });
6593
5856
  },
6594
5857
  children: [
6595
- /* @__PURE__ */ jsx("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
5858
+ /* @__PURE__ */ jsx11("span", { className: "rk-emblema-dot", "data-state": emblemaPairState }),
6596
5859
  "Connect to Emblema\u2026",
6597
- /* @__PURE__ */ jsx("span", { className: "rk-menu-count", children: emblemaPairLabel })
5860
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: emblemaPairLabel })
6598
5861
  ]
6599
5862
  }
6600
5863
  ),
6601
- /* @__PURE__ */ jsx(
5864
+ /* @__PURE__ */ jsx11(
6602
5865
  "button",
6603
5866
  {
6604
5867
  type: "button",
@@ -6611,31 +5874,31 @@ function Panel() {
6611
5874
  )
6612
5875
  ] })
6613
5876
  ] }),
6614
- /* @__PURE__ */ jsx(
5877
+ /* @__PURE__ */ jsx11(
6615
5878
  "button",
6616
5879
  {
6617
5880
  type: "button",
6618
5881
  className: "rk-icon-btn",
6619
5882
  "aria-label": "Keyboard shortcuts",
6620
5883
  onClick: () => dispatch({ type: "SET_CHEATSHEET", open: true }),
6621
- children: /* @__PURE__ */ jsx(IconKeyboard, { size: 15 })
5884
+ children: /* @__PURE__ */ jsx11(IconKeyboard, { size: 15 })
6622
5885
  }
6623
5886
  ),
6624
- /* @__PURE__ */ jsx(
5887
+ /* @__PURE__ */ jsx11(
6625
5888
  "button",
6626
5889
  {
6627
5890
  type: "button",
6628
5891
  className: "rk-icon-btn",
6629
5892
  "aria-label": "Close panel",
6630
5893
  onClick: () => dispatch({ type: "SET_PANEL", open: false }),
6631
- children: /* @__PURE__ */ jsx(IconClose, { size: 14 })
5894
+ children: /* @__PURE__ */ jsx11(IconClose, { size: 14 })
6632
5895
  }
6633
5896
  )
6634
5897
  ] }),
6635
- /* @__PURE__ */ jsxs("div", { className: "rk-view-row", children: [
6636
- /* @__PURE__ */ jsx("div", { className: "rk-view-info", children: /* @__PURE__ */ jsx("div", { className: "rk-view-title", children: "Page view" }) }),
6637
- /* @__PURE__ */ jsxs("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
6638
- /* @__PURE__ */ jsx(
5898
+ /* @__PURE__ */ jsxs10("div", { className: "rk-view-row", children: [
5899
+ /* @__PURE__ */ jsx11("div", { className: "rk-view-info", children: /* @__PURE__ */ jsx11("div", { className: "rk-view-title", children: "Page view" }) }),
5900
+ /* @__PURE__ */ jsxs10("div", { className: "rk-seg rk-view-toggle", role: "group", "aria-label": "Page view (P to toggle)", children: [
5901
+ /* @__PURE__ */ jsx11(
6639
5902
  "button",
6640
5903
  {
6641
5904
  type: "button",
@@ -6645,7 +5908,7 @@ function Panel() {
6645
5908
  children: "Original"
6646
5909
  }
6647
5910
  ),
6648
- /* @__PURE__ */ jsx(
5911
+ /* @__PURE__ */ jsx11(
6649
5912
  "button",
6650
5913
  {
6651
5914
  type: "button",
@@ -6657,8 +5920,8 @@ function Panel() {
6657
5920
  )
6658
5921
  ] })
6659
5922
  ] }),
6660
- /* @__PURE__ */ jsxs("div", { className: "rk-panel-tools", children: [
6661
- /* @__PURE__ */ jsxs(
5923
+ /* @__PURE__ */ jsxs10("div", { className: "rk-panel-tools", children: [
5924
+ /* @__PURE__ */ jsxs10(
6662
5925
  "button",
6663
5926
  {
6664
5927
  type: "button",
@@ -6669,15 +5932,15 @@ function Panel() {
6669
5932
  dispatch({ type: "SET_MODE", mode: "pick" });
6670
5933
  },
6671
5934
  children: [
6672
- /* @__PURE__ */ jsx(IconTarget, { size: 14 }),
5935
+ /* @__PURE__ */ jsx11(IconTarget, { size: 14 }),
6673
5936
  " Pick an element"
6674
5937
  ]
6675
5938
  }
6676
5939
  ),
6677
- /* @__PURE__ */ jsxs("div", { className: "rk-search-row", children: [
6678
- /* @__PURE__ */ jsxs("div", { className: "rk-search", children: [
6679
- /* @__PURE__ */ jsx(IconSearch, { size: 13 }),
6680
- /* @__PURE__ */ jsx(
5940
+ /* @__PURE__ */ jsxs10("div", { className: "rk-search-row", children: [
5941
+ /* @__PURE__ */ jsxs10("div", { className: "rk-search", children: [
5942
+ /* @__PURE__ */ jsx11(IconSearch, { size: 13 }),
5943
+ /* @__PURE__ */ jsx11(
6681
5944
  "input",
6682
5945
  {
6683
5946
  ref: searchRef,
@@ -6689,8 +5952,8 @@ function Panel() {
6689
5952
  }
6690
5953
  )
6691
5954
  ] }),
6692
- /* @__PURE__ */ jsxs("div", { className: "rk-menu-scope rk-menu-anchor", children: [
6693
- /* @__PURE__ */ jsxs(
5955
+ /* @__PURE__ */ jsxs10("div", { className: "rk-menu-scope rk-menu-anchor", children: [
5956
+ /* @__PURE__ */ jsxs10(
6694
5957
  "button",
6695
5958
  {
6696
5959
  type: "button",
@@ -6702,15 +5965,15 @@ function Panel() {
6702
5965
  setSettingsOpen(false);
6703
5966
  },
6704
5967
  children: [
6705
- /* @__PURE__ */ jsx(IconFilter, { size: 13 }),
5968
+ /* @__PURE__ */ jsx11(IconFilter, { size: 13 }),
6706
5969
  "Filter",
6707
- typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ jsx("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
5970
+ typeFilter.size + prioFilter.size + statusFilter.size > 0 && /* @__PURE__ */ jsx11("span", { className: "rk-badge-count", children: typeFilter.size + prioFilter.size + statusFilter.size })
6708
5971
  ]
6709
5972
  }
6710
5973
  ),
6711
- filterOpen && /* @__PURE__ */ jsxs("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
6712
- /* @__PURE__ */ jsx("div", { className: "rk-menu-title", children: "Type" }),
6713
- TYPE_FILTERS.map((type) => /* @__PURE__ */ jsxs(
5974
+ filterOpen && /* @__PURE__ */ jsxs10("div", { className: "rk-menu", role: "menu", "aria-label": "Filter items", children: [
5975
+ /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Type" }),
5976
+ TYPE_FILTERS.map((type) => /* @__PURE__ */ jsxs10(
6714
5977
  "button",
6715
5978
  {
6716
5979
  type: "button",
@@ -6724,16 +5987,16 @@ function Panel() {
6724
5987
  setTypeFilter(next);
6725
5988
  },
6726
5989
  children: [
6727
- /* @__PURE__ */ jsx("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
5990
+ /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${typeFilter.has(type) ? " rk-on" : ""}` }),
6728
5991
  iconForType(type, 12),
6729
- /* @__PURE__ */ jsx("span", { className: "rk-menu-label", children: type }),
6730
- /* @__PURE__ */ jsx("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
5992
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-label", children: type }),
5993
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: typeCounts.get(type) ?? 0 })
6731
5994
  ]
6732
5995
  },
6733
5996
  type
6734
5997
  )),
6735
- /* @__PURE__ */ jsx("div", { className: "rk-menu-title", children: "Priority" }),
6736
- PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ jsxs(
5998
+ /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Priority" }),
5999
+ PRIORITY_FILTERS.map((priority) => /* @__PURE__ */ jsxs10(
6737
6000
  "button",
6738
6001
  {
6739
6002
  type: "button",
@@ -6747,15 +6010,15 @@ function Panel() {
6747
6010
  setPrioFilter(next);
6748
6011
  },
6749
6012
  children: [
6750
- /* @__PURE__ */ jsx("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
6751
- /* @__PURE__ */ jsx("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
6752
- /* @__PURE__ */ jsx("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
6013
+ /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${prioFilter.has(priority) ? " rk-on" : ""}` }),
6014
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-label", children: PRIORITY_LABELS[priority] }),
6015
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: prioCounts.get(priority) ?? 0 })
6753
6016
  ]
6754
6017
  },
6755
6018
  priority
6756
6019
  )),
6757
- /* @__PURE__ */ jsx("div", { className: "rk-menu-title", children: "Status" }),
6758
- STATUSES.map((status) => /* @__PURE__ */ jsxs(
6020
+ /* @__PURE__ */ jsx11("div", { className: "rk-menu-title", children: "Status" }),
6021
+ STATUSES.map((status) => /* @__PURE__ */ jsxs10(
6759
6022
  "button",
6760
6023
  {
6761
6024
  type: "button",
@@ -6769,15 +6032,15 @@ function Panel() {
6769
6032
  setStatusFilter(next);
6770
6033
  },
6771
6034
  children: [
6772
- /* @__PURE__ */ jsx("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
6773
- /* @__PURE__ */ jsx("span", { className: `rk-status-dot rk-st-${status}` }),
6774
- /* @__PURE__ */ jsx("span", { className: "rk-menu-label", children: status }),
6775
- /* @__PURE__ */ jsx("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
6035
+ /* @__PURE__ */ jsx11("span", { className: `rk-menu-check${statusFilter.has(status) ? " rk-on" : ""}` }),
6036
+ /* @__PURE__ */ jsx11("span", { className: `rk-status-dot rk-st-${status}` }),
6037
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-label", children: status }),
6038
+ /* @__PURE__ */ jsx11("span", { className: "rk-menu-count", children: statusCounts.get(status) ?? 0 })
6776
6039
  ]
6777
6040
  },
6778
6041
  status
6779
6042
  )),
6780
- /* @__PURE__ */ jsx(
6043
+ /* @__PURE__ */ jsx11(
6781
6044
  "button",
6782
6045
  {
6783
6046
  type: "button",
@@ -6797,28 +6060,28 @@ function Panel() {
6797
6060
  ] })
6798
6061
  ] })
6799
6062
  ] }),
6800
- preview && unapplied > 0 && /* @__PURE__ */ jsxs("div", { className: "rk-unplaced", role: "status", children: [
6063
+ preview && unapplied > 0 && /* @__PURE__ */ jsxs10("div", { className: "rk-unplaced", role: "status", children: [
6801
6064
  unapplied,
6802
6065
  " saved change",
6803
6066
  unapplied === 1 ? "" : "s",
6804
6067
  " couldn\u2019t be placed on the current page \u2014 the page has changed since they were captured."
6805
6068
  ] }),
6806
- /* @__PURE__ */ jsx("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rk-empty", children: [
6807
- /* @__PURE__ */ jsx("div", { className: "rk-empty-art", children: /* @__PURE__ */ jsx(IconTarget, { size: 22 }) }),
6808
- /* @__PURE__ */ jsx("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
6809
- /* @__PURE__ */ jsx("div", { className: "rk-empty-text", children: items.length === 0 ? "Choose \u201CPick an element\u201D, then click anything on the page. Your suggestions collect here, ready to hand off." : "Try a different search, or clear the filters." })
6810
- ] }) : groups.map(([route, entries]) => /* @__PURE__ */ jsxs("div", { children: [
6811
- /* @__PURE__ */ jsx("div", { className: "rk-group-title", children: route }),
6069
+ /* @__PURE__ */ jsx11("div", { className: "rk-panel-list", children: filtered.length === 0 ? /* @__PURE__ */ jsxs10("div", { className: "rk-empty", children: [
6070
+ /* @__PURE__ */ jsx11("div", { className: "rk-empty-art", children: /* @__PURE__ */ jsx11(IconTarget, { size: 22 }) }),
6071
+ /* @__PURE__ */ jsx11("div", { className: "rk-empty-title", children: items.length === 0 ? "Nothing here yet" : "No matches" }),
6072
+ /* @__PURE__ */ jsx11("div", { className: "rk-empty-text", children: items.length === 0 ? "Choose \u201CPick an element\u201D, then click anything on the page. Your suggestions collect here, ready to hand off." : "Try a different search, or clear the filters." })
6073
+ ] }) : groups.map(([route, entries]) => /* @__PURE__ */ jsxs10("div", { children: [
6074
+ /* @__PURE__ */ jsx11("div", { className: "rk-group-title", children: route }),
6812
6075
  entries.map(({ item, number }) => {
6813
- const active2 = item.id === activeItemId;
6076
+ const active = item.id === activeItemId;
6814
6077
  const orphan = orphanIds.has(item.id);
6815
6078
  const hiddenEl = hiddenIds.has(item.id);
6816
- return /* @__PURE__ */ jsxs("div", { children: [
6817
- /* @__PURE__ */ jsxs(
6079
+ return /* @__PURE__ */ jsxs10("div", { children: [
6080
+ /* @__PURE__ */ jsxs10(
6818
6081
  "div",
6819
6082
  {
6820
6083
  "data-item-id": item.id,
6821
- className: `rk-item${active2 ? " rk-active" : ""}${orphan ? " rk-orphan" : ""}`,
6084
+ className: `rk-item${active ? " rk-active" : ""}${orphan ? " rk-orphan" : ""}`,
6822
6085
  role: "button",
6823
6086
  tabIndex: 0,
6824
6087
  onClick: () => jumpTo(item),
@@ -6829,18 +6092,18 @@ function Panel() {
6829
6092
  }
6830
6093
  },
6831
6094
  children: [
6832
- /* @__PURE__ */ jsx("span", { className: "rk-item-num", children: number }),
6833
- /* @__PURE__ */ jsx("span", { className: "rk-item-icon", children: iconForType(item.type) }),
6834
- /* @__PURE__ */ jsxs("span", { className: "rk-item-body", children: [
6835
- /* @__PURE__ */ jsxs("span", { className: "rk-item-snippet", children: [
6095
+ /* @__PURE__ */ jsx11("span", { className: "rk-item-num", children: number }),
6096
+ /* @__PURE__ */ jsx11("span", { className: "rk-item-icon", children: iconForType(item.type) }),
6097
+ /* @__PURE__ */ jsxs10("span", { className: "rk-item-body", children: [
6098
+ /* @__PURE__ */ jsxs10("span", { className: "rk-item-snippet", children: [
6836
6099
  item.target.tagName,
6837
6100
  " \xB7 ",
6838
6101
  item.target.textSnippet || item.target.selector
6839
6102
  ] }),
6840
- /* @__PURE__ */ jsx("span", { className: "rk-item-change", children: changeSummary(item) }),
6841
- /* @__PURE__ */ jsxs("span", { className: "rk-item-meta", children: [
6842
- /* @__PURE__ */ jsxs("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
6843
- /* @__PURE__ */ jsx(
6103
+ /* @__PURE__ */ jsx11("span", { className: "rk-item-change", children: changeSummary(item) }),
6104
+ /* @__PURE__ */ jsxs10("span", { className: "rk-item-meta", children: [
6105
+ /* @__PURE__ */ jsxs10("span", { className: "rk-author", title: new Date(item.createdAt).toLocaleString(), children: [
6106
+ /* @__PURE__ */ jsx11(
6844
6107
  "span",
6845
6108
  {
6846
6109
  className: "rk-author-dot",
@@ -6848,15 +6111,15 @@ function Panel() {
6848
6111
  }
6849
6112
  ),
6850
6113
  item.author?.name ?? "Unknown",
6851
- /* @__PURE__ */ jsxs("span", { className: "rk-time", children: [
6114
+ /* @__PURE__ */ jsxs10("span", { className: "rk-time", children: [
6852
6115
  " \xB7 ",
6853
6116
  timeAgo(item.createdAt)
6854
6117
  ] })
6855
6118
  ] }),
6856
- item.priority === "must" && /* @__PURE__ */ jsx("span", { className: "rk-badge-mini rk-must", children: "must" }),
6857
- item.status !== "open" && /* @__PURE__ */ jsx("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
6858
- orphan && /* @__PURE__ */ jsx("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
6859
- hiddenEl && /* @__PURE__ */ jsx(
6119
+ item.priority === "must" && /* @__PURE__ */ jsx11("span", { className: "rk-badge-mini rk-must", children: "must" }),
6120
+ item.status !== "open" && /* @__PURE__ */ jsx11("span", { className: `rk-badge-mini rk-${item.status}`, children: item.status }),
6121
+ orphan && /* @__PURE__ */ jsx11("span", { className: "rk-badge-mini rk-orphaned", children: "unanchored" }),
6122
+ hiddenEl && /* @__PURE__ */ jsx11(
6860
6123
  "span",
6861
6124
  {
6862
6125
  className: "rk-badge-mini rk-hidden-el",
@@ -6864,14 +6127,14 @@ function Panel() {
6864
6127
  children: "hidden"
6865
6128
  }
6866
6129
  ),
6867
- item.note && /* @__PURE__ */ jsx("span", { className: "rk-badge-mini", children: "note" }),
6868
- peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ jsxs("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
6130
+ item.note && /* @__PURE__ */ jsx11("span", { className: "rk-badge-mini", children: "note" }),
6131
+ peers.filter((peer) => peer.typingActive && peer.typingItemId === item.id).map((peer) => /* @__PURE__ */ jsxs10("span", { className: "rk-badge-mini", style: { color: peer.color }, children: [
6869
6132
  peer.name || "peer",
6870
6133
  " is writing\u2026"
6871
6134
  ] }, peer.id))
6872
6135
  ] })
6873
6136
  ] }),
6874
- /* @__PURE__ */ jsx("span", { className: "rk-item-actions", children: /* @__PURE__ */ jsx(
6137
+ /* @__PURE__ */ jsx11("span", { className: "rk-item-actions", children: /* @__PURE__ */ jsx11(
6875
6138
  "button",
6876
6139
  {
6877
6140
  type: "button",
@@ -6886,15 +6149,15 @@ function Panel() {
6886
6149
  "info"
6887
6150
  );
6888
6151
  },
6889
- children: /* @__PURE__ */ jsx(IconTrash, { size: 13 })
6152
+ children: /* @__PURE__ */ jsx11(IconTrash, { size: 13 })
6890
6153
  }
6891
6154
  ) })
6892
6155
  ]
6893
6156
  }
6894
6157
  ),
6895
- active2 && /* @__PURE__ */ jsxs("div", { className: "rk-item-edit", children: [
6896
- /* @__PURE__ */ jsxs("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
6897
- /* @__PURE__ */ jsx(
6158
+ active && /* @__PURE__ */ jsxs10("div", { className: "rk-item-edit", children: [
6159
+ /* @__PURE__ */ jsxs10("div", { className: "rk-detail-byline", title: new Date(item.createdAt).toLocaleString(), children: [
6160
+ /* @__PURE__ */ jsx11(
6898
6161
  "span",
6899
6162
  {
6900
6163
  className: "rk-author-dot",
@@ -6905,8 +6168,8 @@ function Panel() {
6905
6168
  " \xB7 ",
6906
6169
  timeAgo(item.createdAt)
6907
6170
  ] }),
6908
- isCopyItem(item) && /* @__PURE__ */ jsx(CopyDiff, { before: item.payload.before, after: item.payload.after }),
6909
- isImageItem(item) && /* @__PURE__ */ jsx(
6171
+ isCopyItem(item) && /* @__PURE__ */ jsx11(CopyDiff, { before: item.payload.before, after: item.payload.after }),
6172
+ isImageItem(item) && /* @__PURE__ */ jsx11(
6910
6173
  ImageDetail,
6911
6174
  {
6912
6175
  beforeSrc: item.payload.beforeSrc,
@@ -6914,7 +6177,7 @@ function Panel() {
6914
6177
  afterDataUrl: item.payload.afterDataUrl
6915
6178
  }
6916
6179
  ),
6917
- isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ jsx(
6180
+ isKnownItemType(item.type) && item.type !== "move" && /* @__PURE__ */ jsx11(
6918
6181
  "textarea",
6919
6182
  {
6920
6183
  className: "rk-textarea",
@@ -6924,7 +6187,7 @@ function Panel() {
6924
6187
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: withPayloadText(item, event.target.value) })
6925
6188
  }
6926
6189
  ),
6927
- /* @__PURE__ */ jsx(
6190
+ /* @__PURE__ */ jsx11(
6928
6191
  "input",
6929
6192
  {
6930
6193
  className: "rk-input",
@@ -6934,9 +6197,9 @@ function Panel() {
6934
6197
  onChange: (event) => dispatch({ type: "UPDATE_ITEM", id: item.id, patch: { note: event.target.value } })
6935
6198
  }
6936
6199
  ),
6937
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
6938
- /* @__PURE__ */ jsxs("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6939
- /* @__PURE__ */ jsx(
6200
+ /* @__PURE__ */ jsxs10("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
6201
+ /* @__PURE__ */ jsxs10("div", { className: "rk-seg", role: "group", "aria-label": "Priority", children: [
6202
+ /* @__PURE__ */ jsx11(
6940
6203
  "button",
6941
6204
  {
6942
6205
  type: "button",
@@ -6945,7 +6208,7 @@ function Panel() {
6945
6208
  children: "Must"
6946
6209
  }
6947
6210
  ),
6948
- /* @__PURE__ */ jsx(
6211
+ /* @__PURE__ */ jsx11(
6949
6212
  "button",
6950
6213
  {
6951
6214
  type: "button",
@@ -6955,7 +6218,7 @@ function Panel() {
6955
6218
  }
6956
6219
  )
6957
6220
  ] }),
6958
- /* @__PURE__ */ jsx("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ jsx(
6221
+ /* @__PURE__ */ jsx11("div", { className: "rk-seg", role: "group", "aria-label": "Status", children: STATUSES.map((status) => /* @__PURE__ */ jsx11(
6959
6222
  "button",
6960
6223
  {
6961
6224
  type: "button",
@@ -6970,22 +6233,22 @@ function Panel() {
6970
6233
  ] }, item.id);
6971
6234
  })
6972
6235
  ] }, route)) }),
6973
- /* @__PURE__ */ jsxs("div", { className: "rk-panel-foot", children: [
6974
- /* @__PURE__ */ jsxs("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
6975
- /* @__PURE__ */ jsx(IconDownload, { size: 13 }),
6236
+ /* @__PURE__ */ jsxs10("div", { className: "rk-panel-foot", children: [
6237
+ /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: onExport, disabled: items.length === 0, children: [
6238
+ /* @__PURE__ */ jsx11(IconDownload, { size: 13 }),
6976
6239
  " Export"
6977
6240
  ] }),
6978
- /* @__PURE__ */ jsxs("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
6979
- /* @__PURE__ */ jsx(IconCopyClipboard, { size: 13 }),
6241
+ /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => void onCopy(), disabled: items.length === 0, children: [
6242
+ /* @__PURE__ */ jsx11(IconCopyClipboard, { size: 13 }),
6980
6243
  " Copy"
6981
6244
  ] }),
6982
- /* @__PURE__ */ jsxs("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
6983
- /* @__PURE__ */ jsx(IconUpload, { size: 13 }),
6245
+ /* @__PURE__ */ jsxs10("button", { type: "button", className: "rk-btn rk-btn-ghost", onClick: () => importRef.current?.click(), children: [
6246
+ /* @__PURE__ */ jsx11(IconUpload, { size: 13 }),
6984
6247
  " Import"
6985
6248
  ] }),
6986
- /* @__PURE__ */ jsx("span", { className: "rk-spacer" }),
6987
- undoStack.length > 0 && /* @__PURE__ */ jsx("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ jsx(IconUndo, { size: 14 }) }),
6988
- /* @__PURE__ */ jsx(
6249
+ /* @__PURE__ */ jsx11("span", { className: "rk-spacer" }),
6250
+ undoStack.length > 0 && /* @__PURE__ */ jsx11("button", { type: "button", className: "rk-icon-btn", "aria-label": "Undo last change (\u2318Z)", onClick: () => dispatch({ type: "UNDO" }), children: /* @__PURE__ */ jsx11(IconUndo, { size: 14 }) }),
6251
+ /* @__PURE__ */ jsx11(
6989
6252
  "button",
6990
6253
  {
6991
6254
  type: "button",
@@ -6996,7 +6259,7 @@ function Panel() {
6996
6259
  children: "Clear"
6997
6260
  }
6998
6261
  ),
6999
- /* @__PURE__ */ jsx(
6262
+ /* @__PURE__ */ jsx11(
7000
6263
  "button",
7001
6264
  {
7002
6265
  type: "button",
@@ -7006,7 +6269,7 @@ function Panel() {
7006
6269
  children: "Exit review"
7007
6270
  }
7008
6271
  ),
7009
- /* @__PURE__ */ jsx(
6272
+ /* @__PURE__ */ jsx11(
7010
6273
  "input",
7011
6274
  {
7012
6275
  ref: importRef,
@@ -7025,6 +6288,10 @@ function Panel() {
7025
6288
  }
7026
6289
  );
7027
6290
  }
6291
+
6292
+ // src/client/components/Presence.tsx
6293
+ import { useEffect as useEffect10, useRef as useRef11 } from "react";
6294
+ import { Fragment as Fragment6, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
7028
6295
  function initials(name) {
7029
6296
  const parts = name.trim().split(/\s+/).filter(Boolean);
7030
6297
  if (parts.length === 0) return "\xB7";
@@ -7034,8 +6301,8 @@ function initials(name) {
7034
6301
  function PresenceStrip() {
7035
6302
  const { peers, syncStatus } = useStore();
7036
6303
  if (syncStatus === "disabled" || peers.length === 0) return null;
7037
- return /* @__PURE__ */ jsxs("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
7038
- peers.slice(0, 5).map((peer) => /* @__PURE__ */ jsx(
6304
+ return /* @__PURE__ */ jsxs11("div", { className: "rk-presence", "aria-label": `${peers.length} reviewer${peers.length === 1 ? "" : "s"} online`, children: [
6305
+ peers.slice(0, 5).map((peer) => /* @__PURE__ */ jsx12(
7039
6306
  "span",
7040
6307
  {
7041
6308
  className: "rk-avatar",
@@ -7045,7 +6312,7 @@ function PresenceStrip() {
7045
6312
  },
7046
6313
  peer.id
7047
6314
  )),
7048
- peers.length > 5 && /* @__PURE__ */ jsxs("span", { className: "rk-avatar rk-avatar-more", children: [
6315
+ peers.length > 5 && /* @__PURE__ */ jsxs11("span", { className: "rk-avatar rk-avatar-more", children: [
7049
6316
  "+",
7050
6317
  peers.length - 5
7051
6318
  ] })
@@ -7053,13 +6320,13 @@ function PresenceStrip() {
7053
6320
  }
7054
6321
  function PeerCursors() {
7055
6322
  const { peers } = useStore();
7056
- const ringRefs = useRef(/* @__PURE__ */ new Map());
7057
- const cursorRefs = useRef(/* @__PURE__ */ new Map());
7058
- const motion = useRef(
6323
+ const ringRefs = useRef11(/* @__PURE__ */ new Map());
6324
+ const cursorRefs = useRef11(/* @__PURE__ */ new Map());
6325
+ const motion = useRef11(
7059
6326
  /* @__PURE__ */ new Map()
7060
6327
  );
7061
- const peersRef = useRef(peers);
7062
- const seen = useRef(/* @__PURE__ */ new Map());
6328
+ const peersRef = useRef11(peers);
6329
+ const seen = useRef11(/* @__PURE__ */ new Map());
7063
6330
  for (const peer of peers) {
7064
6331
  const sig = peer.cursor ? `${peer.cursor.x},${peer.cursor.y},${peer.cursor.ox},${peer.cursor.oy}` : "";
7065
6332
  if (seen.current.get(peer.id) !== sig) {
@@ -7069,7 +6336,7 @@ function PeerCursors() {
7069
6336
  }
7070
6337
  }
7071
6338
  peersRef.current = peers;
7072
- useEffect(() => {
6339
+ useEffect10(() => {
7073
6340
  const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
7074
6341
  let raf = 0;
7075
6342
  let last = performance.now();
@@ -7174,8 +6441,8 @@ function PeerCursors() {
7174
6441
  return () => cancelAnimationFrame(raf);
7175
6442
  }, []);
7176
6443
  if (peers.length === 0) return null;
7177
- return /* @__PURE__ */ jsxs(Fragment, { children: [
7178
- peers.map((peer) => /* @__PURE__ */ jsx(
6444
+ return /* @__PURE__ */ jsxs11(Fragment6, { children: [
6445
+ peers.map((peer) => /* @__PURE__ */ jsx12(
7179
6446
  "div",
7180
6447
  {
7181
6448
  ref: (el) => {
@@ -7187,7 +6454,7 @@ function PeerCursors() {
7187
6454
  },
7188
6455
  `ring-${peer.id}`
7189
6456
  )),
7190
- peers.map((peer, index) => /* @__PURE__ */ jsxs(
6457
+ peers.map((peer, index) => /* @__PURE__ */ jsxs11(
7191
6458
  "div",
7192
6459
  {
7193
6460
  ref: (el) => {
@@ -7197,8 +6464,8 @@ function PeerCursors() {
7197
6464
  className: "rk-peer-cursor",
7198
6465
  style: { opacity: 0 },
7199
6466
  children: [
7200
- /* @__PURE__ */ jsx("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
7201
- /* @__PURE__ */ jsxs(
6467
+ /* @__PURE__ */ jsx12("svg", { width: "14", height: "18", viewBox: "0 0 14 18", "aria-hidden": "true", children: /* @__PURE__ */ jsx12("path", { d: "M1 1l12 7.5-5.2 1.2L5 16z", fill: peer.color, stroke: "rgba(0,0,0,0.25)", strokeWidth: "1" }) }),
6468
+ /* @__PURE__ */ jsxs11(
7202
6469
  "span",
7203
6470
  {
7204
6471
  className: "rk-peer-name",
@@ -7215,17 +6482,21 @@ function PeerCursors() {
7215
6482
  ))
7216
6483
  ] });
7217
6484
  }
6485
+
6486
+ // src/client/components/QuickComment.tsx
6487
+ import { useEffect as useEffect11, useRef as useRef12, useState as useState9 } from "react";
6488
+ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
7218
6489
  function QuickComment() {
7219
6490
  const { quick } = useStore();
7220
6491
  if (!quick) return null;
7221
- return /* @__PURE__ */ jsx(QuickComposerCard, { quick }, quick.id);
6492
+ return /* @__PURE__ */ jsx13(QuickComposerCard, { quick }, quick.id);
7222
6493
  }
7223
6494
  function QuickComposerCard({ quick }) {
7224
6495
  const { route, session } = useStore();
7225
6496
  const dispatch = useDispatch();
7226
- const [text, setText] = useState("");
7227
- const fieldRef = useRef(null);
7228
- useEffect(() => {
6497
+ const [text, setText] = useState9("");
6498
+ const fieldRef = useRef12(null);
6499
+ useEffect11(() => {
7229
6500
  const field = fieldRef.current;
7230
6501
  if (!field) return;
7231
6502
  field.focus();
@@ -7242,7 +6513,7 @@ function QuickComposerCard({ quick }) {
7242
6513
  return;
7243
6514
  }
7244
6515
  const command = /^\/(\S+)$/.exec(trimmed);
7245
- if (command && EFFECTS[command[1]]) {
6516
+ if (command && isEffectCommand(command[1])) {
7246
6517
  try {
7247
6518
  runEffect(command[1], layerRoot);
7248
6519
  sendEffectToRoom(command[1]);
@@ -7278,18 +6549,18 @@ function QuickComposerCard({ quick }) {
7278
6549
  };
7279
6550
  const x = Math.min(Math.max(quick.x, 8), window.innerWidth - 288);
7280
6551
  const y = Math.min(Math.max(quick.y + 12, 8), window.innerHeight - 108);
7281
- return /* @__PURE__ */ jsxs("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
7282
- /* @__PURE__ */ jsxs("div", { className: "rk-quick-head", children: [
7283
- /* @__PURE__ */ jsx(IconComment, { size: 12 }),
7284
- /* @__PURE__ */ jsx("span", { children: "Comment here" }),
7285
- /* @__PURE__ */ jsxs("span", { className: "rk-hint", children: [
7286
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "\u21B5" }),
6552
+ return /* @__PURE__ */ jsxs12("div", { className: "rk-quick", style: { transform: `translate3d(${x}px, ${y}px, 0)` }, role: "dialog", "aria-label": "Quick comment", children: [
6553
+ /* @__PURE__ */ jsxs12("div", { className: "rk-quick-head", children: [
6554
+ /* @__PURE__ */ jsx13(IconComment, { size: 12 }),
6555
+ /* @__PURE__ */ jsx13("span", { children: "Comment here" }),
6556
+ /* @__PURE__ */ jsxs12("span", { className: "rk-hint", children: [
6557
+ /* @__PURE__ */ jsx13("span", { className: "rk-kbd", children: "\u21B5" }),
7287
6558
  " save \xB7 ",
7288
- /* @__PURE__ */ jsx("span", { className: "rk-kbd", children: "esc" }),
6559
+ /* @__PURE__ */ jsx13("span", { className: "rk-kbd", children: "esc" }),
7289
6560
  " cancel"
7290
6561
  ] })
7291
6562
  ] }),
7292
- /* @__PURE__ */ jsx(
6563
+ /* @__PURE__ */ jsx13(
7293
6564
  "textarea",
7294
6565
  {
7295
6566
  ref: fieldRef,
@@ -7303,6 +6574,10 @@ function QuickComposerCard({ quick }) {
7303
6574
  )
7304
6575
  ] });
7305
6576
  }
6577
+
6578
+ // src/client/components/PickLayer.tsx
6579
+ import { useEffect as useEffect12, useRef as useRef13, useState as useState10 } from "react";
6580
+ import { Fragment as Fragment7, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
7306
6581
  function applyRect(el, rect, pad2 = 3) {
7307
6582
  if (!el) return;
7308
6583
  if (!rect) {
@@ -7317,14 +6592,14 @@ function applyRect(el, rect, pad2 = 3) {
7317
6592
  function PickLayer() {
7318
6593
  const { mode, selection, tool } = useStore();
7319
6594
  const dispatch = useDispatch();
7320
- const [hovered, setHovered] = useState(null);
7321
- const outlineRef = useRef(null);
7322
- const labelRef = useRef(null);
7323
- const selectRef = useRef(null);
7324
- const hoveredRef = useRef(null);
6595
+ const [hovered, setHovered] = useState10(null);
6596
+ const outlineRef = useRef13(null);
6597
+ const labelRef = useRef13(null);
6598
+ const selectRef = useRef13(null);
6599
+ const hoveredRef = useRef13(null);
7325
6600
  hoveredRef.current = hovered;
7326
6601
  const picking = mode === "pick";
7327
- useEffect(() => {
6602
+ useEffect12(() => {
7328
6603
  if (!picking) {
7329
6604
  setHovered(null);
7330
6605
  return;
@@ -7368,7 +6643,7 @@ function PickLayer() {
7368
6643
  document.removeEventListener("mousedown", swallow, true);
7369
6644
  };
7370
6645
  }, [picking, tool, dispatch]);
7371
- useEffect(() => {
6646
+ useEffect12(() => {
7372
6647
  if (!picking) return;
7373
6648
  const body = document.body;
7374
6649
  const prev = body.style.cursor;
@@ -7378,7 +6653,7 @@ function PickLayer() {
7378
6653
  if (body.getAttribute("style") === "") body.removeAttribute("style");
7379
6654
  };
7380
6655
  }, [picking, tool]);
7381
- useEffect(() => {
6656
+ useEffect12(() => {
7382
6657
  let raf = 0;
7383
6658
  const tick = () => {
7384
6659
  const hoverEl = hoveredRef.current;
@@ -7404,17 +6679,21 @@ function PickLayer() {
7404
6679
  raf = requestAnimationFrame(tick);
7405
6680
  return () => cancelAnimationFrame(raf);
7406
6681
  }, [picking, selection]);
7407
- return /* @__PURE__ */ jsxs(Fragment, { children: [
7408
- picking && /* @__PURE__ */ jsxs(Fragment, { children: [
7409
- /* @__PURE__ */ jsx("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
7410
- /* @__PURE__ */ jsx("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ jsxs(Fragment, { children: [
7411
- /* @__PURE__ */ jsx("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
7412
- /* @__PURE__ */ jsx("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
7413
- ] }) : /* @__PURE__ */ jsx("span", { children: "Pick an element" }) })
6682
+ return /* @__PURE__ */ jsxs13(Fragment7, { children: [
6683
+ picking && /* @__PURE__ */ jsxs13(Fragment7, { children: [
6684
+ /* @__PURE__ */ jsx14("div", { ref: outlineRef, className: "rk-outline", style: { opacity: 0 } }),
6685
+ /* @__PURE__ */ jsx14("div", { ref: labelRef, className: "rk-outline-label", style: { opacity: 0 }, children: hovered ? /* @__PURE__ */ jsxs13(Fragment7, { children: [
6686
+ /* @__PURE__ */ jsx14("span", { className: "rk-tag", children: hovered.tagName.toLowerCase() }),
6687
+ /* @__PURE__ */ jsx14("span", { children: describeElement(hovered).replace(/^[a-z0-9]+ · /, "") })
6688
+ ] }) : /* @__PURE__ */ jsx14("span", { children: "Pick an element" }) })
7414
6689
  ] }),
7415
- selection && /* @__PURE__ */ jsx("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
6690
+ selection && /* @__PURE__ */ jsx14("div", { ref: selectRef, className: "rk-select-ring", style: { opacity: 0 } })
7416
6691
  ] });
7417
6692
  }
6693
+
6694
+ // src/client/components/Pins.tsx
6695
+ import { useEffect as useEffect13, useMemo as useMemo4, useRef as useRef14, useState as useState11 } from "react";
6696
+ import { Fragment as Fragment8, jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
7418
6697
  var STATUS_LABELS = {
7419
6698
  open: "open",
7420
6699
  accepted: "accepted",
@@ -7426,22 +6705,22 @@ var RERESOLVE_MS = 800;
7426
6705
  function Pins() {
7427
6706
  const { session, route, activeItemId, editor, mode, filter } = useStore();
7428
6707
  const dispatch = useDispatch();
7429
- const [clusters, setClusters] = useState([]);
7430
- const [orphanCount, setOrphanCount] = useState(0);
7431
- const wrapRefs = useRef(/* @__PURE__ */ new Map());
7432
- const visibleCache = useRef(/* @__PURE__ */ new Map());
7433
- const highlightRef = useRef(null);
7434
- const hoveredItems = useRef(null);
7435
- const elementCache = useRef(/* @__PURE__ */ new Map());
7436
- const lastResolve = useRef(0);
7437
- const [newestId, setNewestId] = useState(null);
7438
- const prevCount = useRef(0);
7439
- const items = useMemo(() => {
6708
+ const [clusters, setClusters] = useState11([]);
6709
+ const [orphanCount, setOrphanCount] = useState11(0);
6710
+ const wrapRefs = useRef14(/* @__PURE__ */ new Map());
6711
+ const visibleCache = useRef14(/* @__PURE__ */ new Map());
6712
+ const highlightRef = useRef14(null);
6713
+ const hoveredItems = useRef14(null);
6714
+ const elementCache = useRef14(/* @__PURE__ */ new Map());
6715
+ const lastResolve = useRef14(0);
6716
+ const [newestId, setNewestId] = useState11(null);
6717
+ const prevCount = useRef14(0);
6718
+ const items = useMemo4(() => {
7440
6719
  const all = session?.items ?? [];
7441
6720
  return all.map((item, index) => ({ item, number: index + 1 })).filter(({ item }) => item.route === route);
7442
6721
  }, [session, route]);
7443
6722
  const byId = new Map(items.map(({ item }) => [item.id, item]));
7444
- useEffect(() => {
6723
+ useEffect13(() => {
7445
6724
  const count = session?.items.length ?? 0;
7446
6725
  if (count > prevCount.current && session && count > 0) {
7447
6726
  const id = session.items[count - 1].id;
@@ -7452,7 +6731,7 @@ function Pins() {
7452
6731
  }
7453
6732
  prevCount.current = count;
7454
6733
  }, [session]);
7455
- useEffect(() => {
6734
+ useEffect13(() => {
7456
6735
  let raf = 0;
7457
6736
  let lastSignature = "";
7458
6737
  const tick = (time) => {
@@ -7536,8 +6815,8 @@ function Pins() {
7536
6815
  dispatch({ type: "SET_ACTIVE_ITEM", id });
7537
6816
  dispatch({ type: "SET_PANEL", open: true });
7538
6817
  };
7539
- return /* @__PURE__ */ jsxs(Fragment, { children: [
7540
- /* @__PURE__ */ jsx("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
6818
+ return /* @__PURE__ */ jsxs14(Fragment8, { children: [
6819
+ /* @__PURE__ */ jsx15("div", { ref: highlightRef, className: "rk-hl-ring", style: { opacity: 0 } }),
7541
6820
  clusters.map((cluster) => {
7542
6821
  const isCluster = cluster.ids.length > 1;
7543
6822
  const isActive = activeItemId !== null && cluster.ids.includes(activeItemId);
@@ -7547,7 +6826,7 @@ function Pins() {
7547
6826
  const status = statuses.size === 1 ? members[0]?.status : void 0;
7548
6827
  const dimmed = filter !== null && members.length > 0 && !members.some((item) => itemMatchesFilter(item, filter));
7549
6828
  const statusHint = isCluster ? status ? ` \xB7 all ${STATUS_LABELS[status] ?? status}` : "" : ` \xB7 ${STATUS_LABELS[members[0]?.status ?? "open"] ?? members[0]?.status}`;
7550
- return /* @__PURE__ */ jsx(
6829
+ return /* @__PURE__ */ jsx15(
7551
6830
  "div",
7552
6831
  {
7553
6832
  ref: (el) => {
@@ -7555,7 +6834,7 @@ function Pins() {
7555
6834
  else wrapRefs.current.delete(cluster.key);
7556
6835
  },
7557
6836
  className: `rk-pin-wrap${picking ? " rk-pin-ghost" : ""}`,
7558
- children: /* @__PURE__ */ jsx(
6837
+ children: /* @__PURE__ */ jsx15(
7559
6838
  "button",
7560
6839
  {
7561
6840
  type: "button",
@@ -7577,7 +6856,7 @@ function Pins() {
7577
6856
  cluster.key
7578
6857
  );
7579
6858
  }),
7580
- orphanCount > 0 && /* @__PURE__ */ jsxs(
6859
+ orphanCount > 0 && /* @__PURE__ */ jsxs14(
7581
6860
  "button",
7582
6861
  {
7583
6862
  type: "button",
@@ -7585,7 +6864,7 @@ function Pins() {
7585
6864
  style: { bottom: 16, left: "50%", transform: "translateX(-50%)" },
7586
6865
  onClick: () => dispatch({ type: "SET_PANEL", open: true }),
7587
6866
  children: [
7588
- /* @__PURE__ */ jsx(IconUnlink, { size: 13 }),
6867
+ /* @__PURE__ */ jsx15(IconUnlink, { size: 13 }),
7589
6868
  orphanCount,
7590
6869
  " unanchored item",
7591
6870
  orphanCount === 1 ? "" : "s"
@@ -7594,11 +6873,15 @@ function Pins() {
7594
6873
  )
7595
6874
  ] });
7596
6875
  }
6876
+
6877
+ // src/client/components/Toolbar.tsx
6878
+ import { useEffect as useEffect14, useLayoutEffect as useLayoutEffect3, useRef as useRef15, useState as useState12 } from "react";
6879
+ import { Fragment as Fragment9, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
7597
6880
  var TOOLS = [
7598
- { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ jsx(IconTarget, { size: n }) },
7599
- { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ jsx(IconText, { size: n }) },
7600
- { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ jsx(IconComment, { size: n }) },
7601
- { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ jsx(IconImage, { size: n }) }
6881
+ { tool: "select", hintId: "tool-select", label: "Select", icon: (n) => /* @__PURE__ */ jsx16(IconTarget, { size: n }) },
6882
+ { tool: "copy", hintId: "tool-copy", label: "Rewrite text", icon: (n) => /* @__PURE__ */ jsx16(IconText, { size: n }) },
6883
+ { tool: "comment", hintId: "tool-comment", label: "Comment", icon: (n) => /* @__PURE__ */ jsx16(IconComment, { size: n }) },
6884
+ { tool: "image", hintId: "tool-image", label: "Replace image", icon: (n) => /* @__PURE__ */ jsx16(IconImage, { size: n }) }
7602
6885
  ];
7603
6886
  var POS_KEY2 = "review-kit:toolbar-pos";
7604
6887
  var DRAG_THRESHOLD2 = 6;
@@ -7606,30 +6889,30 @@ function Toolbar() {
7606
6889
  const { selection, editor, peers, syncStatus, mode, tool } = useStore();
7607
6890
  const hasPeers = syncStatus !== "disabled" && peers.length > 0;
7608
6891
  const dispatch = useDispatch();
7609
- const ref = useRef(null);
7610
- const [pos, setPos] = useState(() => readStoredPos(POS_KEY2));
7611
- const drag = useRef(null);
7612
- const [dragging, setDragging] = useState(false);
7613
- const justDragged = useRef(false);
6892
+ const ref = useRef15(null);
6893
+ const [pos, setPos] = useState12(() => readStoredPos(POS_KEY2));
6894
+ const drag = useRef15(null);
6895
+ const [dragging, setDragging] = useState12(false);
6896
+ const justDragged = useRef15(false);
7614
6897
  const visible = editor === null || selection !== null;
7615
6898
  const applyPos = (next, persist) => {
7616
6899
  if (next === null && ref.current) ref.current.style.bottom = "";
7617
6900
  setPos(next);
7618
- storePos(POS_KEY2, next);
6901
+ if (persist) storePos(POS_KEY2, next);
7619
6902
  };
7620
- useLayoutEffect(() => {
6903
+ useLayoutEffect3(() => {
7621
6904
  const el = ref.current;
7622
6905
  if (!el || pos === null) return;
7623
6906
  const clamped = clampPos(pos.x, pos.y, el.offsetWidth, el.offsetHeight);
7624
- if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped);
6907
+ if (clamped.x !== pos.x || clamped.y !== pos.y) applyPos(clamped, true);
7625
6908
  }, [pos, selection, hasPeers, visible]);
7626
- useEffect(() => {
6909
+ useEffect14(() => {
7627
6910
  const onResize = () => {
7628
6911
  const el = ref.current;
7629
6912
  const current = readStoredPos(POS_KEY2);
7630
6913
  if (!el || !current) return;
7631
6914
  const clamped = clampPos(current.x, current.y, el.offsetWidth, el.offsetHeight);
7632
- if (clamped.x !== current.x || clamped.y !== current.y) applyPos(clamped);
6915
+ if (clamped.x !== current.x || clamped.y !== current.y) applyPos(clamped, true);
7633
6916
  };
7634
6917
  window.addEventListener("resize", onResize);
7635
6918
  return () => window.removeEventListener("resize", onResize);
@@ -7694,7 +6977,7 @@ function Toolbar() {
7694
6977
  node.style.left = "0px";
7695
6978
  node.style.top = "0px";
7696
6979
  node.style.translate = `${snapped.x}px ${snapped.y}px`;
7697
- applyPos(snapped);
6980
+ applyPos(snapped, true);
7698
6981
  };
7699
6982
  window.addEventListener("pointermove", onMove, { passive: false });
7700
6983
  window.addEventListener("pointerup", onUp);
@@ -7721,7 +7004,7 @@ function Toolbar() {
7721
7004
  else if (event.key === "ArrowDown") dy = stepSize;
7722
7005
  else return;
7723
7006
  event.preventDefault();
7724
- applyPos(clampPos(rect.left + dx, rect.top + dy, el.offsetWidth, el.offsetHeight));
7007
+ applyPos(clampPos(rect.left + dx, rect.top + dy, el.offsetWidth, el.offsetHeight), true);
7725
7008
  };
7726
7009
  const open = (kind) => {
7727
7010
  if (!selection) return;
@@ -7736,7 +7019,7 @@ function Toolbar() {
7736
7019
  mode === "pick" && selection === null ? "rk-ghost" : ""
7737
7020
  ].filter(Boolean).join(" ");
7738
7021
  const style = pos !== null ? { left: 0, top: 0, translate: `${pos.x}px ${pos.y}px` } : void 0;
7739
- return /* @__PURE__ */ jsxs(
7022
+ return /* @__PURE__ */ jsxs15(
7740
7023
  "div",
7741
7024
  {
7742
7025
  ref,
@@ -7747,27 +7030,27 @@ function Toolbar() {
7747
7030
  onPointerDown,
7748
7031
  onClickCapture,
7749
7032
  children: [
7750
- /* @__PURE__ */ jsx(
7033
+ /* @__PURE__ */ jsx16(
7751
7034
  "button",
7752
7035
  {
7753
7036
  type: "button",
7754
7037
  className: "rk-toolbar-grip",
7755
7038
  "aria-label": "Move the toolbar \u2014 drag, or use arrow keys; double-click to reset its position",
7756
7039
  onKeyDown: onGripKeyDown,
7757
- onDoubleClick: () => applyPos(null),
7758
- children: /* @__PURE__ */ jsxs("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
7759
- /* @__PURE__ */ jsx("circle", { cx: "2", cy: "2", r: "1.1" }),
7760
- /* @__PURE__ */ jsx("circle", { cx: "6", cy: "2", r: "1.1" }),
7761
- /* @__PURE__ */ jsx("circle", { cx: "2", cy: "7", r: "1.1" }),
7762
- /* @__PURE__ */ jsx("circle", { cx: "6", cy: "7", r: "1.1" }),
7763
- /* @__PURE__ */ jsx("circle", { cx: "2", cy: "12", r: "1.1" }),
7764
- /* @__PURE__ */ jsx("circle", { cx: "6", cy: "12", r: "1.1" })
7040
+ onDoubleClick: () => applyPos(null, true),
7041
+ children: /* @__PURE__ */ jsxs15("svg", { width: "8", height: "14", viewBox: "0 0 8 14", "aria-hidden": "true", fill: "currentColor", children: [
7042
+ /* @__PURE__ */ jsx16("circle", { cx: "2", cy: "2", r: "1.1" }),
7043
+ /* @__PURE__ */ jsx16("circle", { cx: "6", cy: "2", r: "1.1" }),
7044
+ /* @__PURE__ */ jsx16("circle", { cx: "2", cy: "7", r: "1.1" }),
7045
+ /* @__PURE__ */ jsx16("circle", { cx: "6", cy: "7", r: "1.1" }),
7046
+ /* @__PURE__ */ jsx16("circle", { cx: "2", cy: "12", r: "1.1" }),
7047
+ /* @__PURE__ */ jsx16("circle", { cx: "6", cy: "12", r: "1.1" })
7765
7048
  ] })
7766
7049
  }
7767
7050
  ),
7768
- /* @__PURE__ */ jsx(PresenceStrip, {}),
7769
- hasPeers && /* @__PURE__ */ jsx("span", { className: "rk-toolbar-sep" }),
7770
- /* @__PURE__ */ jsx("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ jsx(
7051
+ /* @__PURE__ */ jsx16(PresenceStrip, {}),
7052
+ hasPeers && /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7053
+ /* @__PURE__ */ jsx16("div", { className: "rk-seg rk-tool-seg", role: "group", "aria-label": "Tools", children: TOOLS.map((entry) => /* @__PURE__ */ jsx16(
7771
7054
  "button",
7772
7055
  {
7773
7056
  type: "button",
@@ -7779,9 +7062,9 @@ function Toolbar() {
7779
7062
  },
7780
7063
  entry.tool
7781
7064
  )) }),
7782
- selection && /* @__PURE__ */ jsx("span", { className: "rk-toolbar-sep" }),
7783
- selection && /* @__PURE__ */ jsxs(Fragment, { children: [
7784
- /* @__PURE__ */ jsxs(
7065
+ selection && /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7066
+ selection && /* @__PURE__ */ jsxs15(Fragment9, { children: [
7067
+ /* @__PURE__ */ jsxs15(
7785
7068
  "button",
7786
7069
  {
7787
7070
  type: "button",
@@ -7790,12 +7073,12 @@ function Toolbar() {
7790
7073
  title: `Rewrite text${keyHint("tool-copy")}`,
7791
7074
  onClick: () => open("copy"),
7792
7075
  children: [
7793
- /* @__PURE__ */ jsx(IconText, { size: 14 }),
7076
+ /* @__PURE__ */ jsx16(IconText, { size: 14 }),
7794
7077
  " Rewrite"
7795
7078
  ]
7796
7079
  }
7797
7080
  ),
7798
- hasImage && /* @__PURE__ */ jsxs(
7081
+ hasImage && /* @__PURE__ */ jsxs15(
7799
7082
  "button",
7800
7083
  {
7801
7084
  type: "button",
@@ -7804,12 +7087,12 @@ function Toolbar() {
7804
7087
  title: `Replace image${keyHint("tool-image")}`,
7805
7088
  onClick: () => open("image"),
7806
7089
  children: [
7807
- /* @__PURE__ */ jsx(IconImage, { size: 14 }),
7090
+ /* @__PURE__ */ jsx16(IconImage, { size: 14 }),
7808
7091
  " Image"
7809
7092
  ]
7810
7093
  }
7811
7094
  ),
7812
- /* @__PURE__ */ jsxs(
7095
+ /* @__PURE__ */ jsxs15(
7813
7096
  "button",
7814
7097
  {
7815
7098
  type: "button",
@@ -7818,32 +7101,32 @@ function Toolbar() {
7818
7101
  title: `Comment${keyHint("tool-comment")}`,
7819
7102
  onClick: () => open("comment"),
7820
7103
  children: [
7821
- /* @__PURE__ */ jsx(IconComment, { size: 14 }),
7104
+ /* @__PURE__ */ jsx16(IconComment, { size: 14 }),
7822
7105
  " Comment"
7823
7106
  ]
7824
7107
  }
7825
7108
  ),
7826
- /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
7827
- /* @__PURE__ */ jsx(IconStyle, { size: 14 }),
7109
+ /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("style"), children: [
7110
+ /* @__PURE__ */ jsx16(IconStyle, { size: 14 }),
7828
7111
  " Style"
7829
7112
  ] }),
7830
- /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
7831
- /* @__PURE__ */ jsx(IconMove, { size: 14 }),
7113
+ /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn", onClick: () => open("move"), children: [
7114
+ /* @__PURE__ */ jsx16(IconMove, { size: 14 }),
7832
7115
  " Move"
7833
7116
  ] }),
7834
- /* @__PURE__ */ jsxs("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
7835
- /* @__PURE__ */ jsx(IconTrash, { size: 14 }),
7117
+ /* @__PURE__ */ jsxs15("button", { type: "button", role: "menuitem", className: "rk-toolbar-btn rk-danger", onClick: () => open("delete"), children: [
7118
+ /* @__PURE__ */ jsx16(IconTrash, { size: 14 }),
7836
7119
  " Delete"
7837
7120
  ] }),
7838
- /* @__PURE__ */ jsx("span", { className: "rk-toolbar-sep" }),
7839
- /* @__PURE__ */ jsx(
7121
+ /* @__PURE__ */ jsx16("span", { className: "rk-toolbar-sep" }),
7122
+ /* @__PURE__ */ jsx16(
7840
7123
  "button",
7841
7124
  {
7842
7125
  type: "button",
7843
7126
  className: "rk-icon-btn",
7844
7127
  "aria-label": "Deselect",
7845
7128
  onClick: () => dispatch({ type: "SELECT", selection: null }),
7846
- children: /* @__PURE__ */ jsx(IconClose, { size: 13 })
7129
+ children: /* @__PURE__ */ jsx16(IconClose, { size: 13 })
7847
7130
  }
7848
7131
  )
7849
7132
  ] })
@@ -7851,32 +7134,42 @@ function Toolbar() {
7851
7134
  }
7852
7135
  );
7853
7136
  }
7137
+
7138
+ // src/client/components/Toast.tsx
7139
+ import { useEffect as useEffect15 } from "react";
7140
+ import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
7854
7141
  function ToastRow({ toast: toast2 }) {
7855
7142
  const dispatch = useDispatch();
7856
- useEffect(() => {
7143
+ useEffect15(() => {
7857
7144
  const timer = window.setTimeout(() => dispatch({ type: "DISMISS_TOAST", id: toast2.id }), 3500);
7858
7145
  return () => window.clearTimeout(timer);
7859
7146
  }, [toast2.id, dispatch]);
7860
- return /* @__PURE__ */ jsxs("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
7861
- toast2.tone === "success" ? /* @__PURE__ */ jsx(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ jsx(IconWarning, { size: 13 }) : /* @__PURE__ */ jsx(IconComment, { size: 13 }),
7147
+ return /* @__PURE__ */ jsxs16("div", { className: `rk-toast rk-${toast2.tone}`, role: "status", children: [
7148
+ toast2.tone === "success" ? /* @__PURE__ */ jsx17(IconCheck, { size: 13 }) : toast2.tone === "error" ? /* @__PURE__ */ jsx17(IconWarning, { size: 13 }) : /* @__PURE__ */ jsx17(IconComment, { size: 13 }),
7862
7149
  toast2.message
7863
7150
  ] });
7864
7151
  }
7865
7152
  function ToastHost() {
7866
7153
  const { toasts } = useStore();
7867
7154
  if (toasts.length === 0) return null;
7868
- return /* @__PURE__ */ jsx("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ jsx(ToastRow, { toast: toast2 }, toast2.id)) });
7155
+ return /* @__PURE__ */ jsx17("div", { className: "rk-toasts", "aria-live": "polite", children: toasts.map((toast2) => /* @__PURE__ */ jsx17(ToastRow, { toast: toast2 }, toast2.id)) });
7869
7156
  }
7157
+
7158
+ // src/client/App.tsx
7159
+ import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
7870
7160
  function App() {
7871
7161
  const state = useStore();
7872
7162
  const rawDispatch = useDispatch();
7873
7163
  const config = useConfig();
7874
7164
  const dispatch = useRealtimeSync(state, rawDispatch, config, reducer);
7875
- const saveTimer = useRef(null);
7165
+ const saveTimer = useRef16(null);
7876
7166
  const storageKey = config.storageKey ?? defaultStorageKey();
7877
7167
  useAppliedPreview(state, dispatch);
7878
7168
  useEmblemaAutoPush(state, dispatch, config);
7879
- useEffect(() => {
7169
+ useEffect16(() => {
7170
+ setEffectAssetsUrl(config.fxAssetsUrl);
7171
+ }, [config.fxAssetsUrl]);
7172
+ useEffect16(() => {
7880
7173
  let cancelled = false;
7881
7174
  void loadSession(storageKey).then((stored) => {
7882
7175
  if (cancelled) return;
@@ -7894,7 +7187,7 @@ function App() {
7894
7187
  };
7895
7188
  }, [storageKey, config.defaultAuthor, dispatch]);
7896
7189
  const { session } = state;
7897
- useEffect(() => {
7190
+ useEffect16(() => {
7898
7191
  if (!session) return;
7899
7192
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
7900
7193
  saveTimer.current = window.setTimeout(() => {
@@ -7905,9 +7198,9 @@ function App() {
7905
7198
  if (saveTimer.current !== null) window.clearTimeout(saveTimer.current);
7906
7199
  };
7907
7200
  }, [session, storageKey, config]);
7908
- useEffect(() => trackPointer(), []);
7909
- const focusHandled = useRef(false);
7910
- useEffect(() => {
7201
+ useEffect16(() => trackPointer(), []);
7202
+ const focusHandled = useRef16(false);
7203
+ useEffect16(() => {
7911
7204
  if (!session || focusHandled.current) return;
7912
7205
  focusHandled.current = true;
7913
7206
  const id = takeFocusItem();
@@ -7932,7 +7225,7 @@ function App() {
7932
7225
  cancelled = true;
7933
7226
  };
7934
7227
  }, [session, dispatch]);
7935
- useEffect(() => {
7228
+ useEffect16(() => {
7936
7229
  let last = window.location.pathname;
7937
7230
  const check = () => {
7938
7231
  if (window.location.pathname !== last) {
@@ -7947,9 +7240,9 @@ function App() {
7947
7240
  window.removeEventListener("popstate", check);
7948
7241
  };
7949
7242
  }, [dispatch]);
7950
- const stateRef = useRef(state);
7243
+ const stateRef = useRef16(state);
7951
7244
  stateRef.current = state;
7952
- useEffect(() => {
7245
+ useEffect16(() => {
7953
7246
  const onKeyDown = (event) => {
7954
7247
  const current = stateRef.current;
7955
7248
  for (const def of SHORTCUTS) {
@@ -7964,7 +7257,7 @@ function App() {
7964
7257
  document.addEventListener("keydown", onKeyDown);
7965
7258
  return () => document.removeEventListener("keydown", onKeyDown);
7966
7259
  }, [dispatch]);
7967
- useEffect(() => {
7260
+ useEffect16(() => {
7968
7261
  if (!state.editor) return;
7969
7262
  const editorEl = state.editor.element;
7970
7263
  const onPointerDown = (event) => {
@@ -7977,29 +7270,32 @@ function App() {
7977
7270
  }, [state.editor, dispatch]);
7978
7271
  if (!session) return null;
7979
7272
  if (state.hidden) {
7980
- return /* @__PURE__ */ jsx(DispatchContext.Provider, { value: dispatch, children: null });
7273
+ return /* @__PURE__ */ jsx18(DispatchContext.Provider, { value: dispatch, children: null });
7981
7274
  }
7982
- return /* @__PURE__ */ jsxs(DispatchContext.Provider, { value: dispatch, children: [
7983
- /* @__PURE__ */ jsx(PickLayer, {}),
7984
- /* @__PURE__ */ jsx(PeerCursors, {}),
7985
- /* @__PURE__ */ jsx(Toolbar, {}),
7986
- state.editor?.kind === "copy" && /* @__PURE__ */ jsx(InlineEditor, { editor: state.editor }),
7987
- state.editor?.kind === "image" && /* @__PURE__ */ jsx(ImageEditor, { editor: state.editor }),
7988
- state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ jsx(NoteComposer, { editor: state.editor }),
7989
- /* @__PURE__ */ jsx(Pins, {}),
7990
- /* @__PURE__ */ jsx(QuickComment, {}),
7991
- /* @__PURE__ */ jsx(Panel, {}),
7992
- /* @__PURE__ */ jsx(Badge, {}),
7993
- /* @__PURE__ */ jsx(CoachMark, {}),
7994
- /* @__PURE__ */ jsx(CheatSheet, {}),
7995
- /* @__PURE__ */ jsx(EmblemaConnect, {}),
7996
- /* @__PURE__ */ jsx(ConfirmHost, {}),
7997
- /* @__PURE__ */ jsx(ToastHost, {})
7275
+ return /* @__PURE__ */ jsxs17(DispatchContext.Provider, { value: dispatch, children: [
7276
+ /* @__PURE__ */ jsx18(PickLayer, {}),
7277
+ /* @__PURE__ */ jsx18(PeerCursors, {}),
7278
+ /* @__PURE__ */ jsx18(Toolbar, {}),
7279
+ state.editor?.kind === "copy" && /* @__PURE__ */ jsx18(InlineEditor, { editor: state.editor }),
7280
+ state.editor?.kind === "image" && /* @__PURE__ */ jsx18(ImageEditor, { editor: state.editor }),
7281
+ state.editor && state.editor.kind !== "copy" && state.editor.kind !== "image" && /* @__PURE__ */ jsx18(NoteComposer, { editor: state.editor }),
7282
+ /* @__PURE__ */ jsx18(Pins, {}),
7283
+ /* @__PURE__ */ jsx18(QuickComment, {}),
7284
+ /* @__PURE__ */ jsx18(Panel, {}),
7285
+ /* @__PURE__ */ jsx18(Badge, {}),
7286
+ /* @__PURE__ */ jsx18(CoachMark, {}),
7287
+ /* @__PURE__ */ jsx18(CheatSheet, {}),
7288
+ /* @__PURE__ */ jsx18(EmblemaConnect, {}),
7289
+ /* @__PURE__ */ jsx18(ConfirmHost, {}),
7290
+ /* @__PURE__ */ jsx18(ToastHost, {})
7998
7291
  ] });
7999
7292
  }
7293
+
7294
+ // src/client/ReviewKit.tsx
7295
+ import { jsx as jsx19 } from "react/jsx-runtime";
8000
7296
  function ShadowHost({ config }) {
8001
- const [mount, setMount] = useState(null);
8002
- useEffect(() => {
7297
+ const [mount, setMount] = useState13(null);
7298
+ useEffect17(() => {
8003
7299
  const host = document.createElement("div");
8004
7300
  host.setAttribute(HOST_ATTR, "");
8005
7301
  host.style.position = "fixed";
@@ -8035,23 +7331,36 @@ function ShadowHost({ config }) {
8035
7331
  }, []);
8036
7332
  if (!mount) return null;
8037
7333
  return createPortal(
8038
- /* @__PURE__ */ jsx(StoreProvider, { config, children: /* @__PURE__ */ jsx(App, {}) }),
7334
+ /* @__PURE__ */ jsx19(StoreProvider, { config, children: /* @__PURE__ */ jsx19(App, {}) }),
8039
7335
  mount
8040
7336
  );
8041
7337
  }
8042
7338
  function ReviewKit(props) {
8043
- const [active2, setActive] = useState(false);
8044
- useEffect(() => {
8045
- if (!hostAllowed(props.allowedHosts)) return;
8046
- setActive(resolveActivation(props.token));
7339
+ const [active, setActive] = useState13(false);
7340
+ const { enabled, token, allowedHosts, devAutoOn } = props;
7341
+ const hostsKey = allowedHosts?.join("\0") ?? "";
7342
+ useEffect17(() => {
7343
+ if (enabled === false) {
7344
+ setActive(false);
7345
+ return;
7346
+ }
7347
+ setActive(resolveActivation({ token, allowedHosts, devAutoOn, enabled }));
8047
7348
  const onDeactivate = () => setActive(false);
8048
7349
  window.addEventListener(DEACTIVATE_EVENT, onDeactivate);
8049
7350
  return () => window.removeEventListener(DEACTIVATE_EVENT, onDeactivate);
8050
- }, [props.token, props.allowedHosts]);
8051
- if (!active2) return null;
8052
- return /* @__PURE__ */ jsx(ShadowHost, { config: props });
7351
+ }, [enabled, token, devAutoOn, hostsKey]);
7352
+ if (enabled === false || !active) return null;
7353
+ return /* @__PURE__ */ jsx19(ShadowHost, { config: props });
8053
7354
  }
8054
7355
 
8055
- export { ReviewKit, captureTarget, copySessionToClipboard, downloadSession, exportFilename, importSessionFile, resolveTarget, sessionToJson };
8056
- //# sourceMappingURL=chunk-YWBFAV57.js.map
8057
- //# sourceMappingURL=chunk-YWBFAV57.js.map
7356
+ export {
7357
+ captureTarget,
7358
+ resolveTarget,
7359
+ exportFilename,
7360
+ sessionToJson,
7361
+ downloadSession,
7362
+ copySessionToClipboard,
7363
+ importSessionFile,
7364
+ ReviewKit
7365
+ };
7366
+ //# sourceMappingURL=chunk-NSXRFM75.js.map