kritzel-stencil 0.1.75 → 0.1.76

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.
@@ -0,0 +1 @@
1
+ import{p as t,H as e,h as s,d as r,t as o}from"./p-pebXO4LU.js";import{K as n,a as i}from"./p-DXpYcAnT.js";const a=t(class extends e{constructor(t){super(),!1!==t&&this.__registerHost(),this.__attachShadow()}core;showEdgeIndicators=!0;edgeIndicatorPadding=8;remoteCursors=new Map;objectVersion=0;cleanupIntervalId;objectChangeRafId=null;componentDidLoad(){this.core.store.state.objects?.onAwarenessChange((t=>{this.handleAwarenessChange(t)})),this.core.store.state.objects?.onObjectsChange((()=>{this.handleRemoteObjectChange()})),this.cleanupIntervalId=setInterval((()=>{this.cleanupStaleCursors()}),3e3)}disconnectedCallback(){this.cleanupIntervalId&&clearInterval(this.cleanupIntervalId),null!==this.objectChangeRafId&&cancelAnimationFrame(this.objectChangeRafId)}handleAwarenessChange(t){const e=this.core.store.state.objects?.localClientId,s=Date.now(),r=new Map(this.remoteCursors),o=new Set;t.forEach(((t,n)=>{if(n===e)return;if(!t.user)return;o.add(n);const i=t.user,a=t.cursor,l=t.activeObjectId||null,c=t.selectionBox||null,d=r.get(n);r.set(n,{clientId:n,user:i,cursor:a,activeObjectId:l,selectionBox:c,lastUpdated:s,lastCursorMove:!d||!d.cursor!=!a||a&&d.cursor&&(a.x!==d.cursor.x||a.y!==d.cursor.y)?s:d?.lastCursorMove??s})}));for(const t of r.keys())o.has(t)||r.delete(t);this.remoteCursors=r}cleanupStaleCursors(){const t=Date.now();let e=!1;const s=new Map(this.remoteCursors);for(const[r,o]of s)t-o.lastUpdated>3e4&&(s.delete(r),e=!0);e&&(this.remoteCursors=s)}isStale(t){return Date.now()-t.lastCursorMove>1e4}hasActiveDrawingCursors(){for(const t of this.remoteCursors.values())if(t.activeObjectId)return!0;return!1}handleRemoteObjectChange(){this.hasActiveDrawingCursors()&&null===this.objectChangeRafId&&(this.objectChangeRafId=requestAnimationFrame((()=>{this.objectChangeRafId=null,this.objectVersion++})))}getActiveObjectTip(t){const e=this.core.store.state.objects?.findById(t);if(!e)return null;if(e instanceof n&&!e.isCompleted){const t=e.points[e.points.length-1];return t?{x:(t[0]-e.x)/e.scale+e.translateX,y:(t[1]-e.y)/e.scale+e.translateY}:null}return e instanceof i&&!e.isCompleted?{x:(e.endX-e.x)/e.scale+e.translateX,y:(e.endY-e.y)/e.scale+e.translateY}:null}worldToScreen(t,e){const{scale:s,translateX:r,translateY:o}=this.core.store.state;return{x:t*s+r,y:e*s+o}}isInViewport(t,e){const{viewportWidth:s,viewportHeight:r}=this.core.store.state;return t>=0&&t<=s&&e>=0&&e<=r}clampToEdge(t,e){const{viewportWidth:s,viewportHeight:r}=this.core.store.state,o=this.edgeIndicatorPadding,n=Math.max(o,Math.min(s-o,t)),i=Math.max(o,Math.min(r-o,e)),a=n-o,l=s-o-n,c=i-o;let d="top";const h=Math.min(a,l,c,r-o-i);return d=h===a?"left":h===l?"right":h===c?"top":"bottom",{x:n,y:i,angle:Math.atan2(e-i,t-n),edge:d}}getUserDisplayName(t){return t.displayName?t.displayName:t.firstName||t.lastName?[t.firstName,t.lastName].filter(Boolean).join(" "):"Unknown"}render(){const t=Array.from(this.remoteCursors.values());return s(r,{key:"4dd962322c7e955b9038c55cb10f8ffda1e0b246"},t.map((t=>{if(!t.cursor)return null;let e,s=!1;if(t.activeObjectId){const r=this.getActiveObjectTip(t.activeObjectId);r?(s=!0,e=this.worldToScreen(r.x,r.y)):e=this.worldToScreen(t.cursor.x,t.cursor.y)}else e=this.worldToScreen(t.cursor.x,t.cursor.y);const r=this.isInViewport(e.x,e.y),o=this.isStale(t),n=t.user.color||"#6B7280";return r?this.renderCursor(t,e.x,e.y,n,o,s):this.showEdgeIndicators?this.renderEdgeIndicator(t,e.x,e.y,n,o,s):null})),t.map((t=>{if(!t.selectionBox)return null;const e=t.user.color||"#6B7280",r=t.selectionBox,o=this.worldToScreen(r.x,r.y),{scale:n}=this.core.store.state;return s("div",{key:`selection-box-${t.clientId}`,class:"remote-selection-box",style:{transform:`translate(${o.x}px, ${o.y}px)`,width:r.width*n+"px",height:r.height*n+"px",backgroundColor:`color-mix(in srgb, ${e} 20%, transparent)`,borderColor:`color-mix(in srgb, ${e} 50%, transparent)`}})})))}renderCursor(t,e,r,o,n,i){return s("div",{key:`cursor-${t.clientId}`,class:{"awareness-cursor":!0,stale:n,"tracking-object":i},style:{transform:`translate(${e}px, ${r}px)`}},s("svg",{class:"cursor-arrow",width:"20",height:"20",viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg"},s("path",{d:"M5 3L19 12L12 13L9 20L5 3Z",fill:o,stroke:"#ffffff","stroke-width":"1.5","stroke-linejoin":"round"})),s("span",{class:"cursor-label",style:{backgroundColor:o}},this.getUserDisplayName(t.user)))}renderEdgeIndicator(t,e,r,o,n,i){const a=this.clampToEdge(e,r),l=180*a.angle/Math.PI+90;let c=0,d=0;"left"===a.edge?c=20:"right"===a.edge?c=-20:"top"===a.edge?d=20:"bottom"===a.edge&&(d=-20);const h=this.getUserDisplayName(t.user);return s("div",{key:`edge-${t.clientId}`,class:{"edge-indicator":!0,stale:n,"tracking-object":i},style:{transform:`translate(${a.x}px, ${a.y}px)`}},s("svg",{class:"edge-arrow",width:"16",height:"16",viewBox:"0 0 16 16",style:{transform:`rotate(${l}deg)`}},s("path",{d:"M8 1L14 13H2L8 1Z",fill:o,stroke:"#ffffff","stroke-width":"1.5","stroke-linejoin":"round"})),s("span",{class:"edge-label",style:{backgroundColor:o,transform:`translate(${c}px, ${d}px)`}},h))}static get style(){return":host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:9500}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3))}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}"}},[513,"kritzel-awareness-cursors",{core:[16],showEdgeIndicators:[4,"show-edge-indicators"],edgeIndicatorPadding:[2,"edge-indicator-padding"],remoteCursors:[32],objectVersion:[32]}]);function l(){"undefined"!=typeof customElements&&["kritzel-awareness-cursors"].forEach((t=>{"kritzel-awareness-cursors"===t&&(customElements.get(o(t))||customElements.define(o(t),a))}))}export{a as K,l as d}
@@ -154,7 +154,7 @@ const KritzelAvatar = class {
154
154
  };
155
155
  KritzelAvatar.style = kritzelAvatarCss();
156
156
 
157
- const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:9500}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0.3}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0.3}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3))}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
157
+ const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:9500}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3))}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
158
158
 
159
159
  const STALE_THRESHOLD_MS = 10_000;
160
160
  const REMOVE_THRESHOLD_MS = 30_000;
@@ -205,6 +205,10 @@ const KritzelAwarenessCursors = class {
205
205
  const cursor = state.cursor;
206
206
  const activeObjectId = state.activeObjectId || null;
207
207
  const selectionBox = state.selectionBox || null;
208
+ const existing = updated.get(clientId);
209
+ const cursorMoved = !existing ||
210
+ !existing.cursor !== !cursor ||
211
+ (cursor && existing.cursor && (cursor.x !== existing.cursor.x || cursor.y !== existing.cursor.y));
208
212
  updated.set(clientId, {
209
213
  clientId,
210
214
  user,
@@ -212,6 +216,7 @@ const KritzelAwarenessCursors = class {
212
216
  activeObjectId,
213
217
  selectionBox,
214
218
  lastUpdated: now,
219
+ lastCursorMove: cursorMoved ? now : (existing?.lastCursorMove ?? now),
215
220
  });
216
221
  });
217
222
  // Remove cursors for disconnected clients
@@ -237,7 +242,7 @@ const KritzelAwarenessCursors = class {
237
242
  }
238
243
  }
239
244
  isStale(cursor) {
240
- return Date.now() - cursor.lastUpdated > STALE_THRESHOLD_MS;
245
+ return Date.now() - cursor.lastCursorMove > STALE_THRESHOLD_MS;
241
246
  }
242
247
  hasActiveDrawingCursors() {
243
248
  for (const cursor of this.remoteCursors.values()) {
@@ -326,7 +331,7 @@ const KritzelAwarenessCursors = class {
326
331
  }
327
332
  render() {
328
333
  const cursors = Array.from(this.remoteCursors.values());
329
- return (h(Host, { key: 'b8676d9a7a3d4a79ea8ad9763355ecf4c2f90e17' }, cursors.map(remoteCursor => {
334
+ return (h(Host, { key: '4dd962322c7e955b9038c55cb10f8ffda1e0b246' }, cursors.map(remoteCursor => {
330
335
  if (!remoteCursor.cursor)
331
336
  return null;
332
337
  // When a remote user is actively drawing, derive cursor position from
@@ -19818,6 +19823,14 @@ class KritzelViewport {
19818
19823
  startX = 0;
19819
19824
  /** Starting Y position for pan/zoom gestures */
19820
19825
  startY = 0;
19826
+ /** Minimum movement distance (in screen pixels) before broadcasting touch cursor position */
19827
+ static TOUCH_CURSOR_BROADCAST_THRESHOLD = 5;
19828
+ /** Screen X position where the current touch interaction started */
19829
+ _touchStartScreenX = 0;
19830
+ /** Screen Y position where the current touch interaction started */
19831
+ _touchStartScreenY = 0;
19832
+ /** Whether the touch movement threshold has been exceeded for cursor broadcasting */
19833
+ _touchCursorBroadcastActive = false;
19821
19834
  /**
19822
19835
  * Creates a new KritzelViewport instance and initializes viewport state.
19823
19836
  * Sets up debounced handlers for viewport updates and scaling end events.
@@ -19945,6 +19958,11 @@ class KritzelViewport {
19945
19958
  }
19946
19959
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
19947
19960
  const activePointers = Array.from(this._core.store.state.pointers.values());
19961
+ if (activePointers.length === 1) {
19962
+ this._touchStartScreenX = event.clientX;
19963
+ this._touchStartScreenY = event.clientY;
19964
+ this._touchCursorBroadcastActive = false;
19965
+ }
19948
19966
  if (activePointers.length === 2) {
19949
19967
  this._core.store.state.objects?.clearCursorPosition();
19950
19968
  const currentPath = this._core.store.currentPath;
@@ -20014,7 +20032,16 @@ class KritzelViewport {
20014
20032
  else {
20015
20033
  this._core.store.state.pointerX = (xRelativeToHost - this._core.store.state.translateX) / this._core.store.state.scale;
20016
20034
  this._core.store.state.pointerY = (yRelativeToHost - this._core.store.state.translateY) / this._core.store.state.scale;
20017
- this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
20035
+ if (!this._touchCursorBroadcastActive) {
20036
+ const dx = event.clientX - this._touchStartScreenX;
20037
+ const dy = event.clientY - this._touchStartScreenY;
20038
+ if (Math.sqrt(dx * dx + dy * dy) >= KritzelViewport.TOUCH_CURSOR_BROADCAST_THRESHOLD) {
20039
+ this._touchCursorBroadcastActive = true;
20040
+ }
20041
+ }
20042
+ if (this._touchCursorBroadcastActive) {
20043
+ this._core.store.state.objects?.updateCursorPosition(this._core.store.state.pointerX, this._core.store.state.pointerY);
20044
+ }
20018
20045
  }
20019
20046
  if (activePointers.length === 2) {
20020
20047
  const firstTouchX = activePointers[0].clientX - this._core.store.offsetX;
@@ -20067,6 +20094,7 @@ class KritzelViewport {
20067
20094
  }
20068
20095
  }
20069
20096
  if (event.pointerType === 'touch' || event.pointerType === 'pen') {
20097
+ this._touchCursorBroadcastActive = false;
20070
20098
  if (this._core.store.state.pointers.size === 0) {
20071
20099
  this._debounceEndScaling();
20072
20100
  }
@@ -21346,6 +21374,42 @@ class KritzelObjectMap {
21346
21374
  }
21347
21375
  this._awareness.setLocalStateField('selectionBox', null);
21348
21376
  }
21377
+ /**
21378
+ * Removes selection groups whose owner is no longer present in awareness.
21379
+ * Called when remote clients disconnect to prevent orphaned selection groups
21380
+ * from persisting in the workspace state.
21381
+ */
21382
+ removeOrphanedSelectionGroups() {
21383
+ if (!this._awareness) {
21384
+ return;
21385
+ }
21386
+ const states = this._awareness.getStates();
21387
+ const activeUserIds = new Set();
21388
+ states.forEach(state => {
21389
+ const userId = state.user?.id;
21390
+ if (userId) {
21391
+ activeUserIds.add(userId);
21392
+ }
21393
+ });
21394
+ const localUserId = this._core?.user?.id;
21395
+ const orphanedGroups = this.quadtree.filter(o => o instanceof KritzelSelectionGroup
21396
+ && o.userId != null
21397
+ && o.userId !== localUserId
21398
+ && !activeUserIds.has(o.userId));
21399
+ for (const group of orphanedGroups) {
21400
+ this.quadtree.remove(o => o.id === group.id);
21401
+ this._idMap.delete(group.id);
21402
+ if (this._objectsMap) {
21403
+ this._ydoc.transact(() => {
21404
+ this._objectsMap.delete(group.id);
21405
+ }, 'local');
21406
+ }
21407
+ }
21408
+ if (orphanedGroups.length > 0) {
21409
+ this._core?.store.invalidateSelectionCache();
21410
+ this._core?.rerender();
21411
+ }
21412
+ }
21349
21413
  /**
21350
21414
  * Registers a callback to be invoked when the awareness state changes.
21351
21415
  * The callback receives the full awareness states map.
@@ -21508,7 +21572,11 @@ class KritzelObjectMap {
21508
21572
  }
21509
21573
  // Subscribe to awareness changes
21510
21574
  if (this._awareness) {
21511
- this._awarenessChangeHandler = () => {
21575
+ this._awarenessChangeHandler = (change) => {
21576
+ // Clean up selection groups belonging to disconnected users
21577
+ if (change.removed.length > 0) {
21578
+ this.removeOrphanedSelectionGroups();
21579
+ }
21512
21580
  const now = Date.now();
21513
21581
  const timeSinceLastEmit = now - this._lastAwarenessEmitTime;
21514
21582
  // Clear any pending timeout since we have a new event
@@ -21771,11 +21839,27 @@ class KritzelObjectMap {
21771
21839
  }
21772
21840
  this.quadtree.reset();
21773
21841
  this._idMap.clear();
21774
- this._objectsMap.forEach(serialized => {
21842
+ const localUserId = this._core?.user?.id;
21843
+ const staleSelectionGroupIds = [];
21844
+ this._objectsMap.forEach((serialized, key) => {
21775
21845
  const object = this._reviver.revive(serialized);
21846
+ // Remove remote selection groups on startup — they are transient UI state
21847
+ // that should not survive an app restart. The owning user's session is gone.
21848
+ if (object instanceof KritzelSelectionGroup && object.userId != null && object.userId !== localUserId) {
21849
+ staleSelectionGroupIds.push(key);
21850
+ return;
21851
+ }
21776
21852
  this.quadtree.insert(object);
21777
21853
  this._idMap.set(object.id, object);
21778
21854
  });
21855
+ // Clean up stale remote selection groups from Yjs
21856
+ if (staleSelectionGroupIds.length > 0) {
21857
+ this._ydoc.transact(() => {
21858
+ for (const id of staleSelectionGroupIds) {
21859
+ this._objectsMap.delete(id);
21860
+ }
21861
+ }, 'local');
21862
+ }
21779
21863
  }
21780
21864
  /**
21781
21865
  * Resets the object map by clearing both the local quadtree and the Yjs objects map.
@@ -28415,7 +28499,7 @@ const KritzelPortal = class {
28415
28499
  * This file is auto-generated by the version bump scripts.
28416
28500
  * Do not modify manually.
28417
28501
  */
28418
- const KRITZEL_VERSION = '0.1.75';
28502
+ const KRITZEL_VERSION = '0.1.76';
28419
28503
 
28420
28504
  const kritzelSettingsCss = () => `:host{display:contents}kritzel-dialog{--kritzel-dialog-body-padding:0;--kritzel-dialog-width-large:800px;--kritzel-dialog-height-large:500px}.footer-button{padding:8px 16px;border-radius:6px;cursor:pointer;font-size:14px}.cancel-button{border:1px solid #ebebeb;background:#fff;color:inherit}.cancel-button:hover{background:#f5f5f5}.settings-content{padding:0}.settings-content h3{margin:0 0 16px 0;font-size:18px;font-weight:600;color:var(--kritzel-settings-content-heading-color, #333333)}.settings-content p{margin:0;font-size:14px;color:var(--kritzel-settings-content-text-color, #666666);line-height:1.5}.settings-group{display:flex;flex-direction:column;gap:24px}.settings-item{display:flex;flex-direction:column;gap:8px}.settings-row{display:flex;align-items:center;justify-content:space-between;gap:16px}.settings-label{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.settings-description{font-size:12px;color:var(--kritzel-settings-description-color, #888888);margin:0;line-height:1.4}.shortcuts-list{display:flex;flex-direction:column;gap:24px}.shortcuts-category{display:flex;flex-direction:column;gap:8px}.shortcuts-category-title{font-size:14px;font-weight:600;color:var(--kritzel-settings-label-color, #333333);margin:0 0 4px 0}.shortcuts-group{display:flex;flex-direction:column;gap:4px}.shortcut-item{display:flex;justify-content:space-between;align-items:center;padding:6px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-item-bg, rgba(0, 0, 0, 0.02))}.shortcut-label{font-size:14px;color:var(--kritzel-settings-content-text-color, #666666)}.shortcut-key{font-family:monospace;font-size:12px;padding:2px 8px;border-radius:4px;background:var(--kritzel-settings-shortcut-key-bg, #f0f0f0);color:var(--kritzel-settings-shortcut-key-color, #333333);border:1px solid var(--kritzel-settings-shortcut-key-border, #ddd)}`;
28421
28505