@syntrologie/adapt-content 2.8.0-canary.93 → 2.8.0-canary.95

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 (19) hide show
  1. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts +9 -1
  2. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts.map +1 -1
  3. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.js +27 -1
  4. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +1 -1
  5. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +28 -17
  6. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts.map +1 -1
  7. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.js +18 -17
  8. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts +1 -1
  9. package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts +110 -0
  10. package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts.map +1 -0
  11. package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.js +476 -0
  12. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +2 -0
  13. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +1 -1
  14. package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +1 -0
  15. package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts +1 -0
  16. package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts.map +1 -1
  17. package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.js +1 -0
  18. package/node_modules/@syntrologie/shared-editor-ui/package.json +1 -0
  19. package/package.json +1 -1
@@ -54,9 +54,17 @@ export declare class AnchorPickerLit extends LitElement {
54
54
  updated(changed: Map<string, unknown>): void;
55
55
  private _attachDocumentListeners;
56
56
  private _detachDocumentListeners;
57
+ /**
58
+ * Resolve the shadow host this picker lives inside, if any. When the picker
59
+ * is mounted inside a shadow root (e.g. the editor SDK mounts inside
60
+ * `<smart-canvas>`'s shadow), `document.elementFromPoint` returns the shadow
61
+ * *host*, not the host page element beneath. Temporarily disabling pointer
62
+ * events on the host lets hit-testing fall through to the host page.
63
+ */
64
+ private _getShadowHost;
57
65
  private _handleMouseMove;
58
66
  private _handleClick;
59
67
  private _handleKeyDown;
60
- render(): import("lit-html").TemplateResult<1> | typeof nothing;
68
+ render(): typeof nothing | import("lit-html").TemplateResult<1>;
61
69
  }
62
70
  //# sourceMappingURL=AnchorPickerLit.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnchorPickerLit.d.ts","sourceRoot":"","sources":["../../src/components/AnchorPickerLit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAQ,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAahD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAgBD,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,UAAU;;;;;;;;;;;;;;;;;;;MAOxB;IAEF,QAAQ,UAAS;IACjB,iBAAiB,UAAS;IAC1B,eAAe,EAAE,MAAM,CAA4B;IAGnD,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,gBAAgB,CAAM;IAG9B,OAAO,CAAC,UAAU,CAA+B;IAIxC,gBAAgB;IAIhB,iBAAiB,IAAI,IAAI;IAOzB,oBAAoB,IAAI,IAAI;IAK5B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAiBrD,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,gBAAgB,CAoCtB;IAEF,OAAO,CAAC,YAAY,CAwBlB;IAEF,OAAO,CAAC,cAAc,CAUpB;IAIO,MAAM;CA0GhB"}
1
+ {"version":3,"file":"AnchorPickerLit.d.ts","sourceRoot":"","sources":["../../src/components/AnchorPickerLit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAQ,UAAU,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAahD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAgBD,qBAAa,eAAgB,SAAQ,UAAU;IAC7C,OAAgB,UAAU;;;;;;;;;;;;;;;;;;;MAOxB;IAEF,QAAQ,UAAS;IACjB,iBAAiB,UAAS;IAC1B,eAAe,EAAE,MAAM,CAA4B;IAGnD,OAAO,CAAC,eAAe,CAAwB;IAC/C,OAAO,CAAC,gBAAgB,CAAM;IAG9B,OAAO,CAAC,UAAU,CAA+B;IAIxC,gBAAgB;IAIhB,iBAAiB,IAAI,IAAI;IAOzB,oBAAoB,IAAI,IAAI;IAK5B,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAiBrD,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,wBAAwB;IAQhC;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,gBAAgB,CA+CtB;IAEF,OAAO,CAAC,YAAY,CAwBlB;IAEF,OAAO,CAAC,cAAc,CAUpB;IAIO,MAAM;CA0GhB"}
@@ -38,16 +38,28 @@ export class AnchorPickerLit extends LitElement {
38
38
  this._hoveredSelector = '';
39
39
  // DOM ref for the overlay
40
40
  this._overlayEl = null;
41
- // ---- EVENT HANDLERS ----
42
41
  this._handleMouseMove = (e) => {
43
42
  const overlay = this._overlayEl;
43
+ const shadowHost = this._getShadowHost();
44
44
  // In passthrough mode the overlay is already pointerEvents:'none',
45
45
  // so elementFromPoint can see through it -- no toggle needed.
46
46
  // In non-passthrough mode, temporarily disable to detect elements underneath.
47
47
  if (overlay && !this.passthroughClicks) {
48
48
  overlay.style.pointerEvents = 'none';
49
49
  }
50
+ // If we're inside a shadow root, temporarily hide the host so
51
+ // elementFromPoint can reach the host page underneath. `pointer-events`
52
+ // doesn't work here because any fixed-position descendant inside the
53
+ // shadow tree with pointer-events:auto (e.g. a canvas overlay) causes
54
+ // elementFromPoint to return the shadow host regardless of what the
55
+ // host's own pointer-events is. `visibility:hidden` removes the entire
56
+ // subtree from hit-testing without triggering layout reflow.
57
+ const prevHostVis = shadowHost?.style.visibility;
58
+ if (shadowHost)
59
+ shadowHost.style.visibility = 'hidden';
50
60
  const elementAtPoint = document.elementFromPoint(e.clientX, e.clientY);
61
+ if (shadowHost)
62
+ shadowHost.style.visibility = prevHostVis ?? '';
51
63
  if (overlay && !this.passthroughClicks) {
52
64
  overlay.style.pointerEvents = 'auto';
53
65
  }
@@ -141,6 +153,20 @@ export class AnchorPickerLit extends LitElement {
141
153
  document.removeEventListener('click', this._handleClick, true);
142
154
  document.removeEventListener('keydown', this._handleKeyDown, true);
143
155
  }
156
+ // ---- EVENT HANDLERS ----
157
+ /**
158
+ * Resolve the shadow host this picker lives inside, if any. When the picker
159
+ * is mounted inside a shadow root (e.g. the editor SDK mounts inside
160
+ * `<smart-canvas>`'s shadow), `document.elementFromPoint` returns the shadow
161
+ * *host*, not the host page element beneath. Temporarily disabling pointer
162
+ * events on the host lets hit-testing fall through to the host page.
163
+ */
164
+ _getShadowHost() {
165
+ const root = this.getRootNode();
166
+ if (root instanceof ShadowRoot)
167
+ return root.host;
168
+ return null;
169
+ }
144
170
  // ---- RENDER ----
145
171
  render() {
146
172
  if (!this.isActive)
@@ -1 +1 @@
1
- {"version":3,"file":"EditorPanelShell.d.ts","sourceRoot":"","sources":["../../src/components/EditorPanelShell.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AA8MD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAmB,EACnB,QAAQ,GACT,EAAE,qBAAqB,2CAySvB"}
1
+ {"version":3,"file":"EditorPanelShell.d.ts","sourceRoot":"","sources":["../../src/components/EditorPanelShell.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AA8MD,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,QAAQ,EACR,OAAO,EACP,MAAmB,EACnB,QAAQ,GACT,EAAE,qBAAqB,2CAwSvB"}
@@ -1,17 +1,28 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * EditorPanelShell — moveable, resizable wrapper for editor sidebars.
4
+ *
5
+ * Renders:
6
+ * 1. A fixed FAB button (Syntrologie logo) at the panel's bottom-left corner.
7
+ * Click toggles open/minimized. Drag moves the entire panel.
8
+ * 2. A resizable panel container with semi-transparent background and backdrop blur.
9
+ * Supports docked (left/right edge, full height) and free-floating modes.
10
+ * Position, size, and dock state persist in localStorage.
11
+ */
12
+ import { panelShell } from '@syntro/design-system';
2
13
  import { useCallback, useEffect, useRef, useState } from 'react';
3
14
  import { cn } from '../cn';
4
15
  // =============================================================================
5
16
  // Constants
6
17
  // =============================================================================
7
18
  const STORAGE_KEY = 'syntro:editor-panel';
8
- const MIN_WIDTH = 480;
9
- const MIN_HEIGHT = 400;
10
- const FAB_SIZE = 56;
11
- const FAB_INSET = 12;
12
- const SNAP_THRESHOLD = 20;
13
- const DRAG_THRESHOLD = 5;
14
- const HANDLE_SIZE = 8;
19
+ const MIN_WIDTH = panelShell.behavior.minWidth;
20
+ const MIN_HEIGHT = panelShell.behavior.minHeight;
21
+ const FAB_SIZE = panelShell.fab.size;
22
+ const FAB_INSET = panelShell.fab.inset;
23
+ const SNAP_THRESHOLD = panelShell.behavior.snapThreshold;
24
+ const DRAG_THRESHOLD = panelShell.behavior.dragThreshold;
25
+ const HANDLE_SIZE = panelShell.behavior.handleSize;
15
26
  // =============================================================================
16
27
  // Logo
17
28
  // =============================================================================
@@ -313,12 +324,14 @@ export function EditorPanelShell({ isOpen, onToggle, panelId, zIndex = 214748364
313
324
  const panelStyle = {
314
325
  position: 'fixed',
315
326
  zIndex,
316
- background: 'linear-gradient(160deg, rgba(7,8,10,0.84) 0%, rgba(14,17,20,0.88) 45%, rgba(15,19,24,0.84) 100%)',
327
+ background: panelShell.background,
328
+ backdropFilter: `blur(${panelShell.backdropBlur})`,
329
+ WebkitBackdropFilter: `blur(${panelShell.backdropBlur})`,
317
330
  boxShadow: isDocked
318
331
  ? geometry.docked === 'right'
319
- ? '-20px 0 60px rgba(0,0,0,0.5)'
320
- : '20px 0 60px rgba(0,0,0,0.5)'
321
- : '0 8px 60px rgba(0,0,0,0.5)',
332
+ ? panelShell.shadows.dockedRight
333
+ : panelShell.shadows.dockedLeft
334
+ : panelShell.shadows.floating,
322
335
  // Prevent wheel scroll from chaining to the page behind the panel.
323
336
  // overflow-y: auto makes this a scroll container so overscroll-behavior applies.
324
337
  overflowY: 'auto',
@@ -350,16 +363,14 @@ export function EditorPanelShell({ isOpen, onToggle, panelId, zIndex = 214748364
350
363
  width: FAB_SIZE,
351
364
  height: FAB_SIZE,
352
365
  borderRadius: '50%',
353
- border: '2px solid #b72e2a',
366
+ border: panelShell.fab.border,
354
367
  cursor: 'grab',
355
368
  display: 'flex',
356
369
  alignItems: 'center',
357
370
  justifyContent: 'center',
358
- background: '#000000',
359
- color: '#ffffff',
360
- boxShadow: isOpen
361
- ? '0 4px 24px rgba(0,0,0,0.6), 0 0 0 2px rgba(255,255,255,0.08)'
362
- : '0 4px 24px rgba(0,0,0,0.6)',
371
+ background: panelShell.fab.background,
372
+ color: panelShell.fab.color,
373
+ boxShadow: isOpen ? panelShell.fab.shadowOpen : panelShell.fab.shadowClosed,
363
374
  transition: 'box-shadow 150ms ease',
364
375
  touchAction: 'none',
365
376
  userSelect: 'none',
@@ -1 +1 @@
1
- {"version":3,"file":"EditorPanelShellLit.d.ts","sourceRoot":"","sources":["../../src/components/EditorPanelShellLit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAQ,UAAU,EAAW,MAAM,KAAK,CAAC;AA8OhD,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,OAAgB,UAAU;;;;;;;;;;;;;MAIxB;IAEF,MAAM,UAAS;IACf,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,SAAc;IAGpB,OAAO,CAAC,SAAS,CAAuC;IAGxD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,MAAM,CAAkC;IAGhD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAA8B;IAGjD,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,eAAe,CAAuC;IAI9D,OAAO,KAAK,WAAW,GAEtB;IAEQ,gBAAgB;IAIhB,iBAAiB,IAAI,IAAI;IAMzB,oBAAoB,IAAI,IAAI;IAK5B,OAAO,IAAI,IAAI;IAQxB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,mBAAmB,CAEzB;IAEF,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,qBAAqB;IA+B7B,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,wBAAwB;IAqChC,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,eAAe;IAwCvB,OAAO,CAAC,aAAa;IA+BZ,MAAM;CAsDhB"}
1
+ {"version":3,"file":"EditorPanelShellLit.d.ts","sourceRoot":"","sources":["../../src/components/EditorPanelShellLit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAQ,UAAU,EAAW,MAAM,KAAK,CAAC;AA8OhD,qBAAa,mBAAoB,SAAQ,UAAU;IACjD,OAAgB,UAAU;;;;;;;;;;;;;MAIxB;IAEF,MAAM,UAAS;IACf,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,MAAM,SAAc;IAGpB,OAAO,CAAC,SAAS,CAAuC;IAGxD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,MAAM,CAAkC;IAGhD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAA8B;IAGjD,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,iBAAiB,CAAkB;IAC3C,OAAO,CAAC,eAAe,CAAuC;IAI9D,OAAO,KAAK,WAAW,GAEtB;IAEQ,gBAAgB;IAIhB,iBAAiB,IAAI,IAAI;IAMzB,oBAAoB,IAAI,IAAI;IAK5B,OAAO,IAAI,IAAI;IAQxB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,mBAAmB,CAEzB;IAEF,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,qBAAqB;IA+B7B,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,wBAAwB;IAgBhC,OAAO,CAAC,wBAAwB;IAqChC,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,eAAe;IAyCvB,OAAO,CAAC,aAAa;IA6BZ,MAAM;CAsDhB"}
@@ -14,19 +14,20 @@
14
14
  * - localStorage persistence of geometry
15
15
  * - Viewport clamping on window resize
16
16
  */
17
+ import { panelShell } from '@syntro/design-system';
17
18
  import { html, LitElement, nothing } from 'lit';
18
19
  import { styleMap } from 'lit/directives/style-map.js';
19
20
  // =============================================================================
20
21
  // Constants
21
22
  // =============================================================================
22
23
  const STORAGE_KEY = 'syntro:editor-panel';
23
- const MIN_WIDTH = 480;
24
- const MIN_HEIGHT = 400;
25
- const FAB_SIZE = 56;
26
- const FAB_INSET = 12;
27
- const SNAP_THRESHOLD = 20;
28
- const DRAG_THRESHOLD = 5;
29
- const HANDLE_SIZE = 8;
24
+ const MIN_WIDTH = panelShell.behavior.minWidth;
25
+ const MIN_HEIGHT = panelShell.behavior.minHeight;
26
+ const FAB_SIZE = panelShell.fab.size;
27
+ const FAB_INSET = panelShell.fab.inset;
28
+ const SNAP_THRESHOLD = panelShell.behavior.snapThreshold;
29
+ const DRAG_THRESHOLD = panelShell.behavior.dragThreshold;
30
+ const HANDLE_SIZE = panelShell.behavior.handleSize;
30
31
  // =============================================================================
31
32
  // Geometry Helpers
32
33
  // =============================================================================
@@ -411,12 +412,14 @@ export class EditorPanelShellLit extends LitElement {
411
412
  // Re-enable pointer events — parent editor container uses pointer-events:none
412
413
  // so clicks pass through to elements beneath (e.g., the launcher FAB).
413
414
  pointerEvents: 'auto',
414
- background: 'linear-gradient(160deg, rgba(7,8,10,0.84) 0%, rgba(14,17,20,0.88) 45%, rgba(15,19,24,0.84) 100%)',
415
+ background: panelShell.background,
416
+ backdropFilter: `blur(${panelShell.backdropBlur})`,
417
+ webkitBackdropFilter: `blur(${panelShell.backdropBlur})`,
415
418
  boxShadow: isDocked
416
419
  ? geo.docked === 'right'
417
- ? '-20px 0 60px rgba(0,0,0,0.5)'
418
- : '20px 0 60px rgba(0,0,0,0.5)'
419
- : '0 8px 60px rgba(0,0,0,0.5)',
420
+ ? panelShell.shadows.dockedRight
421
+ : panelShell.shadows.dockedLeft
422
+ : panelShell.shadows.floating,
420
423
  overflowY: 'auto',
421
424
  overscrollBehavior: 'contain',
422
425
  };
@@ -452,16 +455,14 @@ export class EditorPanelShellLit extends LitElement {
452
455
  width: `${FAB_SIZE}px`,
453
456
  height: `${FAB_SIZE}px`,
454
457
  borderRadius: '50%',
455
- border: '2px solid #b72e2a',
458
+ border: panelShell.fab.border,
456
459
  cursor: 'grab',
457
460
  display: 'flex',
458
461
  alignItems: 'center',
459
462
  justifyContent: 'center',
460
- background: '#000000',
461
- color: '#ffffff',
462
- boxShadow: this.isOpen
463
- ? '0 4px 24px rgba(0,0,0,0.6), 0 0 0 2px rgba(255,255,255,0.08)'
464
- : '0 4px 24px rgba(0,0,0,0.6)',
463
+ background: panelShell.fab.background,
464
+ color: panelShell.fab.color,
465
+ boxShadow: this.isOpen ? panelShell.fab.shadowOpen : panelShell.fab.shadowClosed,
465
466
  transition: 'box-shadow 150ms ease',
466
467
  touchAction: 'none',
467
468
  userSelect: 'none',
@@ -85,6 +85,6 @@ export declare class ElementHighlightLit extends LitElement {
85
85
  private _handleClick;
86
86
  private _handleKeyDown;
87
87
  private _handleRemove;
88
- render(): import("lit-html").TemplateResult<1> | typeof nothing;
88
+ render(): typeof nothing | import("lit-html").TemplateResult<1>;
89
89
  }
90
90
  //# sourceMappingURL=ElementHighlightLit.d.ts.map
@@ -0,0 +1,110 @@
1
+ /**
2
+ * PanelShellController — Lit ReactiveController for draggable/resizable panels.
3
+ *
4
+ * Owns geometry state (position, size, dock), pointer-event handlers for the
5
+ * drag FAB and 8 resize handles, localStorage persistence, and viewport
6
+ * clamping. A consuming LitElement calls `attachFab(el)` / `attachHandle(el, dir)`
7
+ * on its rendered DOM to hook up pointer events, and reads `panelStyles` /
8
+ * `fabStyles` to position the panel + FAB.
9
+ *
10
+ * Why a controller, not a wrapper element: `<slot>`-based wrapper elements
11
+ * don't compose well in light DOM (Lit wipes children on render), and shadow
12
+ * DOM breaks Playwright child selectors used by E2E tests. A ReactiveController
13
+ * is the Lit-idiomatic way to share stateful logic across elements.
14
+ *
15
+ * Emits one callback: `onToggle` when the FAB is pressed without dragging.
16
+ */
17
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
18
+ export interface PanelGeometry {
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ docked: 'left' | 'right' | null;
24
+ }
25
+ export type ResizeDir = 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw';
26
+ export interface PanelShellControllerOptions {
27
+ /** Unique storage key for geometry persistence. */
28
+ storageKey?: string;
29
+ /** Minimum width when resizing or undocked. Default 480. */
30
+ minWidth?: number;
31
+ /** Minimum height when resizing or undocked. Default 400. */
32
+ minHeight?: number;
33
+ /** Pixels the FAB must move before a press is treated as a drag. Default 5. */
34
+ dragThreshold?: number;
35
+ /** Edge-snap distance for docking. Default 20. */
36
+ snapThreshold?: number;
37
+ /** Size of the FAB in pixels. Default 56. */
38
+ fabSize?: number;
39
+ /** Inset of the FAB from the panel's top-left corner. Default 12. */
40
+ fabInset?: number;
41
+ /** Default geometry on first load when nothing is in storage. */
42
+ defaultGeometry?: Partial<PanelGeometry>;
43
+ /** Called when the FAB is pressed without dragging. */
44
+ onToggle?: () => void;
45
+ }
46
+ export interface ResizeHandleDef {
47
+ dir: ResizeDir;
48
+ style: Record<string, string>;
49
+ cursor: string;
50
+ }
51
+ export declare function makeResizeHandles(): ResizeHandleDef[];
52
+ export declare class PanelShellController implements ReactiveController {
53
+ private readonly host;
54
+ private readonly opts;
55
+ private _geometry;
56
+ private _panelEl;
57
+ private _fabEl;
58
+ private _isDragging;
59
+ private _hasMoved;
60
+ private _dragOffset;
61
+ private _startPanelPos;
62
+ private _pendingGeo;
63
+ private _activeResize;
64
+ private _resizeStartMouse;
65
+ private _resizeStartGeo;
66
+ constructor(host: ReactiveControllerHost, options?: PanelShellControllerOptions);
67
+ /** Current geometry (read-only snapshot). */
68
+ get geometry(): PanelGeometry;
69
+ /** Pixel rect of the panel, resolving docked positions. */
70
+ get rect(): {
71
+ top: number;
72
+ left: number;
73
+ width: number;
74
+ height: number;
75
+ };
76
+ /** Pixel right edge of the panel (useful for positioning adjacent UI). */
77
+ get rightEdge(): number;
78
+ /** Set geometry directly — useful for testing or external state sync. */
79
+ setGeometry(geo: PanelGeometry): void;
80
+ /** Reset geometry to default (and clear persisted state). */
81
+ reset(): void;
82
+ /** Styles to apply to the panel element. */
83
+ panelStyles(): Record<string, string>;
84
+ /** Styles to apply to the FAB button. `isOpen` adjusts the shadow. */
85
+ fabStyles(isOpen: boolean): Record<string, string>;
86
+ /** Direction descriptors for the 8 resize handles. */
87
+ get handles(): ResizeHandleDef[];
88
+ /** Bind the current panel element so the controller can direct-write styles during drag/resize. */
89
+ attachPanel(el: HTMLElement | null): void;
90
+ /** Bind the current FAB element. */
91
+ attachFab(el: HTMLElement | null): void;
92
+ hostConnected(): void;
93
+ hostDisconnected(): void;
94
+ handleFabPointerDown: (e: PointerEvent) => void;
95
+ handleFabPointerMove: (e: PointerEvent) => void;
96
+ handleFabPointerUp: (e: PointerEvent) => void;
97
+ handleResizePointerDown: (dir: ResizeDir, e: PointerEvent) => void;
98
+ handleResizePointerMove: (e: PointerEvent) => void;
99
+ handleResizePointerUp: (e: PointerEvent) => void;
100
+ private _commit;
101
+ private _writeStylesDuringGesture;
102
+ private _onWindowResize;
103
+ private _clampToViewport;
104
+ private _undockGeometry;
105
+ private _panelRect;
106
+ private _loadGeometry;
107
+ private _saveGeometry;
108
+ private _resolveDefaultGeometry;
109
+ }
110
+ //# sourceMappingURL=PanelShellController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PanelShellController.d.ts","sourceRoot":"","sources":["../../src/controllers/PanelShellController.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,476 @@
1
+ /**
2
+ * PanelShellController — Lit ReactiveController for draggable/resizable panels.
3
+ *
4
+ * Owns geometry state (position, size, dock), pointer-event handlers for the
5
+ * drag FAB and 8 resize handles, localStorage persistence, and viewport
6
+ * clamping. A consuming LitElement calls `attachFab(el)` / `attachHandle(el, dir)`
7
+ * on its rendered DOM to hook up pointer events, and reads `panelStyles` /
8
+ * `fabStyles` to position the panel + FAB.
9
+ *
10
+ * Why a controller, not a wrapper element: `<slot>`-based wrapper elements
11
+ * don't compose well in light DOM (Lit wipes children on render), and shadow
12
+ * DOM breaks Playwright child selectors used by E2E tests. A ReactiveController
13
+ * is the Lit-idiomatic way to share stateful logic across elements.
14
+ *
15
+ * Emits one callback: `onToggle` when the FAB is pressed without dragging.
16
+ */
17
+ import { panelShell } from '@syntro/design-system';
18
+ // =============================================================================
19
+ // Constants
20
+ // =============================================================================
21
+ const DEFAULTS = {
22
+ minWidth: panelShell.behavior.minWidth,
23
+ minHeight: panelShell.behavior.minHeight,
24
+ dragThreshold: panelShell.behavior.dragThreshold,
25
+ snapThreshold: panelShell.behavior.snapThreshold,
26
+ fabSize: panelShell.fab.size,
27
+ fabInset: panelShell.fab.inset,
28
+ storageKey: 'syntro:editor-panel',
29
+ };
30
+ const HANDLE_SIZE = panelShell.behavior.handleSize;
31
+ // =============================================================================
32
+ // Helpers
33
+ // =============================================================================
34
+ export function makeResizeHandles() {
35
+ return [
36
+ {
37
+ dir: 'n',
38
+ style: {
39
+ top: '0',
40
+ left: `${HANDLE_SIZE}px`,
41
+ right: `${HANDLE_SIZE}px`,
42
+ height: `${HANDLE_SIZE}px`,
43
+ },
44
+ cursor: 'ns-resize',
45
+ },
46
+ {
47
+ dir: 's',
48
+ style: {
49
+ bottom: '0',
50
+ left: `${HANDLE_SIZE}px`,
51
+ right: `${HANDLE_SIZE}px`,
52
+ height: `${HANDLE_SIZE}px`,
53
+ },
54
+ cursor: 'ns-resize',
55
+ },
56
+ {
57
+ dir: 'e',
58
+ style: {
59
+ right: '0',
60
+ top: `${HANDLE_SIZE}px`,
61
+ bottom: `${HANDLE_SIZE}px`,
62
+ width: `${HANDLE_SIZE}px`,
63
+ },
64
+ cursor: 'ew-resize',
65
+ },
66
+ {
67
+ dir: 'w',
68
+ style: {
69
+ left: '0',
70
+ top: `${HANDLE_SIZE}px`,
71
+ bottom: `${HANDLE_SIZE}px`,
72
+ width: `${HANDLE_SIZE}px`,
73
+ },
74
+ cursor: 'ew-resize',
75
+ },
76
+ {
77
+ dir: 'ne',
78
+ style: {
79
+ top: '0',
80
+ right: '0',
81
+ width: `${HANDLE_SIZE * 2}px`,
82
+ height: `${HANDLE_SIZE * 2}px`,
83
+ },
84
+ cursor: 'nesw-resize',
85
+ },
86
+ {
87
+ dir: 'nw',
88
+ style: {
89
+ top: '0',
90
+ left: '0',
91
+ width: `${HANDLE_SIZE * 2}px`,
92
+ height: `${HANDLE_SIZE * 2}px`,
93
+ },
94
+ cursor: 'nwse-resize',
95
+ },
96
+ {
97
+ dir: 'se',
98
+ style: {
99
+ bottom: '0',
100
+ right: '0',
101
+ width: `${HANDLE_SIZE * 2}px`,
102
+ height: `${HANDLE_SIZE * 2}px`,
103
+ },
104
+ cursor: 'nwse-resize',
105
+ },
106
+ {
107
+ dir: 'sw',
108
+ style: {
109
+ bottom: '0',
110
+ left: '0',
111
+ width: `${HANDLE_SIZE * 2}px`,
112
+ height: `${HANDLE_SIZE * 2}px`,
113
+ },
114
+ cursor: 'nesw-resize',
115
+ },
116
+ ];
117
+ }
118
+ // =============================================================================
119
+ // Controller
120
+ // =============================================================================
121
+ export class PanelShellController {
122
+ constructor(host, options = {}) {
123
+ this._panelEl = null;
124
+ this._fabEl = null;
125
+ // Drag state
126
+ this._isDragging = false;
127
+ this._hasMoved = false;
128
+ this._dragOffset = { x: 0, y: 0 };
129
+ this._startPanelPos = { x: 0, y: 0 };
130
+ this._pendingGeo = null;
131
+ // Resize state
132
+ this._activeResize = null;
133
+ this._resizeStartMouse = { x: 0, y: 0 };
134
+ // ---- FAB pointer handlers ----
135
+ this.handleFabPointerDown = (e) => {
136
+ this._isDragging = true;
137
+ this._hasMoved = false;
138
+ const rect = this.rect;
139
+ this._startPanelPos = { x: rect.left, y: rect.top };
140
+ this._dragOffset = { x: e.clientX - rect.left, y: e.clientY - rect.top };
141
+ try {
142
+ e.target.setPointerCapture(e.pointerId);
143
+ }
144
+ catch {
145
+ // best-effort
146
+ }
147
+ e.preventDefault();
148
+ };
149
+ this.handleFabPointerMove = (e) => {
150
+ if (!this._isDragging)
151
+ return;
152
+ const newX = e.clientX - this._dragOffset.x;
153
+ const newY = e.clientY - this._dragOffset.y;
154
+ if (!this._hasMoved) {
155
+ const dx = Math.abs(newX - this._startPanelPos.x);
156
+ const dy = Math.abs(newY - this._startPanelPos.y);
157
+ if (dx < this.opts.dragThreshold && dy < this.opts.dragThreshold)
158
+ return;
159
+ this._hasMoved = true;
160
+ }
161
+ const geo = this._geometry;
162
+ const height = geo.docked ? window.innerHeight : geo.height;
163
+ this._writeStylesDuringGesture({ x: newX, y: newY, width: geo.width, height });
164
+ this._pendingGeo = { x: newX, y: newY, width: geo.width, height, docked: null };
165
+ };
166
+ this.handleFabPointerUp = (e) => {
167
+ try {
168
+ e.target.releasePointerCapture(e.pointerId);
169
+ }
170
+ catch {
171
+ // best-effort
172
+ }
173
+ const wasDragging = this._hasMoved;
174
+ this._isDragging = false;
175
+ this._hasMoved = false;
176
+ if (!wasDragging) {
177
+ this.opts.onToggle?.();
178
+ return;
179
+ }
180
+ if (this._pendingGeo) {
181
+ const geo = this._pendingGeo;
182
+ if (geo.x <= this.opts.snapThreshold) {
183
+ this._commit({ ...geo, docked: 'left', x: 0, y: 0 });
184
+ }
185
+ else if (geo.x + geo.width >= window.innerWidth - this.opts.snapThreshold) {
186
+ this._commit({ ...geo, docked: 'right', x: 0, y: 0 });
187
+ }
188
+ else {
189
+ this._commit(geo);
190
+ }
191
+ this._pendingGeo = null;
192
+ }
193
+ };
194
+ // ---- Resize pointer handlers ----
195
+ this.handleResizePointerDown = (dir, e) => {
196
+ let geo = this._geometry;
197
+ if (geo.docked) {
198
+ geo = this._undockGeometry(geo);
199
+ this._commit(geo);
200
+ }
201
+ this._activeResize = dir;
202
+ this._resizeStartMouse = { x: e.clientX, y: e.clientY };
203
+ this._resizeStartGeo = { ...geo };
204
+ try {
205
+ e.target.setPointerCapture(e.pointerId);
206
+ }
207
+ catch {
208
+ // best-effort
209
+ }
210
+ e.preventDefault();
211
+ };
212
+ this.handleResizePointerMove = (e) => {
213
+ if (!this._activeResize)
214
+ return;
215
+ const dx = e.clientX - this._resizeStartMouse.x;
216
+ const dy = e.clientY - this._resizeStartMouse.y;
217
+ const dir = this._activeResize;
218
+ const start = this._resizeStartGeo;
219
+ let { x, y, width, height } = start;
220
+ if (dir.includes('e'))
221
+ width = Math.max(this.opts.minWidth, start.width + dx);
222
+ if (dir.includes('w')) {
223
+ const newW = Math.max(this.opts.minWidth, start.width - dx);
224
+ x = start.x + start.width - newW;
225
+ width = newW;
226
+ }
227
+ if (dir.includes('s'))
228
+ height = Math.max(this.opts.minHeight, start.height + dy);
229
+ if (dir.includes('n')) {
230
+ const newH = Math.max(this.opts.minHeight, start.height - dy);
231
+ y = start.y + start.height - newH;
232
+ height = newH;
233
+ }
234
+ this._writeStylesDuringGesture({ x, y, width, height });
235
+ this._pendingGeo = { x, y, width, height, docked: null };
236
+ };
237
+ this.handleResizePointerUp = (e) => {
238
+ try {
239
+ e.target.releasePointerCapture(e.pointerId);
240
+ }
241
+ catch {
242
+ // best-effort
243
+ }
244
+ this._activeResize = null;
245
+ if (this._pendingGeo) {
246
+ this._commit(this._pendingGeo);
247
+ this._pendingGeo = null;
248
+ }
249
+ };
250
+ this._onWindowResize = () => {
251
+ this._commit(this._clampToViewport(this._geometry));
252
+ };
253
+ this.host = host;
254
+ this.opts = {
255
+ storageKey: options.storageKey ?? DEFAULTS.storageKey,
256
+ minWidth: options.minWidth ?? DEFAULTS.minWidth,
257
+ minHeight: options.minHeight ?? DEFAULTS.minHeight,
258
+ dragThreshold: options.dragThreshold ?? DEFAULTS.dragThreshold,
259
+ snapThreshold: options.snapThreshold ?? DEFAULTS.snapThreshold,
260
+ fabSize: options.fabSize ?? DEFAULTS.fabSize,
261
+ fabInset: options.fabInset ?? DEFAULTS.fabInset,
262
+ onToggle: options.onToggle,
263
+ defaultGeometry: this._resolveDefaultGeometry(options.defaultGeometry),
264
+ };
265
+ this._geometry = this.opts.defaultGeometry;
266
+ this._resizeStartGeo = this._geometry;
267
+ host.addController(this);
268
+ }
269
+ // ---- Public API ----
270
+ /** Current geometry (read-only snapshot). */
271
+ get geometry() {
272
+ return this._geometry;
273
+ }
274
+ /** Pixel rect of the panel, resolving docked positions. */
275
+ get rect() {
276
+ return this._panelRect(this._geometry);
277
+ }
278
+ /** Pixel right edge of the panel (useful for positioning adjacent UI). */
279
+ get rightEdge() {
280
+ const r = this.rect;
281
+ return r.left + r.width;
282
+ }
283
+ /** Set geometry directly — useful for testing or external state sync. */
284
+ setGeometry(geo) {
285
+ this._geometry = geo;
286
+ this._saveGeometry(geo);
287
+ this.host.requestUpdate();
288
+ }
289
+ /** Reset geometry to default (and clear persisted state). */
290
+ reset() {
291
+ this._geometry = this.opts.defaultGeometry;
292
+ try {
293
+ localStorage.removeItem(this.opts.storageKey);
294
+ }
295
+ catch {
296
+ // ignore
297
+ }
298
+ this.host.requestUpdate();
299
+ }
300
+ /** Styles to apply to the panel element. */
301
+ panelStyles() {
302
+ const geo = this._geometry;
303
+ const blur = `blur(${panelShell.backdropBlur})`;
304
+ const shadow = geo.docked === 'right'
305
+ ? panelShell.shadows.dockedRight
306
+ : geo.docked === 'left'
307
+ ? panelShell.shadows.dockedLeft
308
+ : panelShell.shadows.floating;
309
+ const styles = {
310
+ position: 'fixed',
311
+ pointerEvents: 'auto',
312
+ background: panelShell.background,
313
+ backdropFilter: blur,
314
+ webkitBackdropFilter: blur,
315
+ boxShadow: shadow,
316
+ overflow: 'hidden',
317
+ };
318
+ if (geo.docked === 'left') {
319
+ styles.top = '0';
320
+ styles.left = '0';
321
+ styles.width = `${geo.width}px`;
322
+ styles.height = '100vh';
323
+ }
324
+ else if (geo.docked === 'right') {
325
+ styles.top = '0';
326
+ styles.right = '0';
327
+ styles.width = `${geo.width}px`;
328
+ styles.height = '100vh';
329
+ }
330
+ else {
331
+ styles.top = `${geo.y}px`;
332
+ styles.left = `${geo.x}px`;
333
+ styles.width = `${geo.width}px`;
334
+ styles.height = `${geo.height}px`;
335
+ }
336
+ return styles;
337
+ }
338
+ /** Styles to apply to the FAB button. `isOpen` adjusts the shadow. */
339
+ fabStyles(isOpen) {
340
+ const rect = this.rect;
341
+ return {
342
+ position: 'fixed',
343
+ left: `${rect.left + this.opts.fabInset}px`,
344
+ top: `${rect.top + this.opts.fabInset}px`,
345
+ pointerEvents: 'auto',
346
+ width: `${this.opts.fabSize}px`,
347
+ height: `${this.opts.fabSize}px`,
348
+ borderRadius: '50%',
349
+ border: panelShell.fab.border,
350
+ cursor: 'grab',
351
+ display: 'flex',
352
+ alignItems: 'center',
353
+ justifyContent: 'center',
354
+ background: panelShell.fab.background,
355
+ color: panelShell.fab.color,
356
+ boxShadow: isOpen ? panelShell.fab.shadowOpen : panelShell.fab.shadowClosed,
357
+ transition: 'box-shadow 150ms ease',
358
+ touchAction: 'none',
359
+ userSelect: 'none',
360
+ webkitUserSelect: 'none',
361
+ };
362
+ }
363
+ /** Direction descriptors for the 8 resize handles. */
364
+ get handles() {
365
+ return makeResizeHandles();
366
+ }
367
+ /** Bind the current panel element so the controller can direct-write styles during drag/resize. */
368
+ attachPanel(el) {
369
+ this._panelEl = el;
370
+ }
371
+ /** Bind the current FAB element. */
372
+ attachFab(el) {
373
+ this._fabEl = el;
374
+ }
375
+ // ---- ReactiveController lifecycle ----
376
+ hostConnected() {
377
+ this._geometry = this._loadGeometry();
378
+ this.host.requestUpdate();
379
+ if (typeof window !== 'undefined') {
380
+ window.addEventListener('resize', this._onWindowResize);
381
+ }
382
+ }
383
+ hostDisconnected() {
384
+ if (typeof window !== 'undefined') {
385
+ window.removeEventListener('resize', this._onWindowResize);
386
+ }
387
+ }
388
+ // ---- Internals ----
389
+ _commit(geo) {
390
+ this._geometry = geo;
391
+ this._saveGeometry(geo);
392
+ this.host.requestUpdate();
393
+ }
394
+ _writeStylesDuringGesture({ x, y, width, height, }) {
395
+ if (this._panelEl) {
396
+ this._panelEl.style.left = `${x}px`;
397
+ this._panelEl.style.top = `${y}px`;
398
+ this._panelEl.style.right = 'auto';
399
+ this._panelEl.style.width = `${width}px`;
400
+ this._panelEl.style.height = `${height}px`;
401
+ }
402
+ if (this._fabEl) {
403
+ this._fabEl.style.left = `${x + this.opts.fabInset}px`;
404
+ this._fabEl.style.top = `${y + this.opts.fabInset}px`;
405
+ }
406
+ }
407
+ _clampToViewport(geo) {
408
+ if (geo.docked)
409
+ return geo;
410
+ const vw = window.innerWidth;
411
+ const vh = window.innerHeight;
412
+ return {
413
+ ...geo,
414
+ x: Math.max(-(geo.width - this.opts.fabSize), Math.min(geo.x, vw - this.opts.fabSize)),
415
+ y: Math.max(-this.opts.fabInset, Math.min(geo.y, vh - this.opts.fabSize - this.opts.fabInset)),
416
+ };
417
+ }
418
+ _undockGeometry(geo) {
419
+ const r = this._panelRect(geo);
420
+ return { x: r.left, y: r.top, width: geo.width, height: r.height, docked: null };
421
+ }
422
+ _panelRect(geo) {
423
+ if (geo.docked === 'left') {
424
+ return { top: 0, left: 0, width: geo.width, height: window.innerHeight };
425
+ }
426
+ if (geo.docked === 'right') {
427
+ return {
428
+ top: 0,
429
+ left: window.innerWidth - geo.width,
430
+ width: geo.width,
431
+ height: window.innerHeight,
432
+ };
433
+ }
434
+ const { x: left, y: top, width, height } = geo;
435
+ return { top, left, width, height };
436
+ }
437
+ _loadGeometry() {
438
+ if (typeof window === 'undefined')
439
+ return this.opts.defaultGeometry;
440
+ try {
441
+ const raw = localStorage.getItem(this.opts.storageKey);
442
+ if (!raw)
443
+ return this.opts.defaultGeometry;
444
+ const p = JSON.parse(raw);
445
+ return {
446
+ x: typeof p.x === 'number' ? p.x : 0,
447
+ y: typeof p.y === 'number' ? p.y : 0,
448
+ width: Math.max(this.opts.minWidth, typeof p.width === 'number' ? p.width : this.opts.minWidth),
449
+ height: Math.max(this.opts.minHeight, typeof p.height === 'number' ? p.height : window.innerHeight),
450
+ docked: p.docked === 'left' || p.docked === 'right' ? p.docked : null,
451
+ };
452
+ }
453
+ catch {
454
+ return this.opts.defaultGeometry;
455
+ }
456
+ }
457
+ _saveGeometry(geo) {
458
+ try {
459
+ localStorage.setItem(this.opts.storageKey, JSON.stringify(geo));
460
+ }
461
+ catch {
462
+ // ignore
463
+ }
464
+ }
465
+ _resolveDefaultGeometry(partial) {
466
+ const w = typeof window !== 'undefined' ? window : null;
467
+ const base = {
468
+ x: 0,
469
+ y: 0,
470
+ width: DEFAULTS.minWidth,
471
+ height: w ? w.innerHeight : 800,
472
+ docked: 'right',
473
+ };
474
+ return { ...base, ...(partial ?? {}) };
475
+ }
476
+ }
@@ -23,6 +23,8 @@ export { EmptyState } from './components/EmptyState';
23
23
  export { GroupHeader } from './components/GroupHeader';
24
24
  export type { TriggerJourneyProps } from './components/TriggerJourney';
25
25
  export { TriggerJourney } from './components/TriggerJourney';
26
+ export type { PanelGeometry, PanelShellControllerOptions, ResizeDir, ResizeHandleDef, } from './controllers/PanelShellController';
27
+ export { makeResizeHandles, PanelShellController } from './controllers/PanelShellController';
26
28
  export type { ConditionProgress, FormattedCondition } from './formatConditionLabel';
27
29
  export { formatConditionLabel } from './formatConditionLabel';
28
30
  export { useElementRect } from './hooks/useElementRect';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAE1B,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACL,KAAK,UAAU,EACf,0BAA0B,EAC1B,oBAAoB,EACpB,KAAK,cAAc,GACpB,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAC1B,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,YAAY,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EACV,aAAa,EACb,2BAA2B,EAC3B,SAAS,EACT,eAAe,GAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC7F,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACL,KAAK,UAAU,EACf,0BAA0B,EAC1B,oBAAoB,EACpB,KAAK,cAAc,GACpB,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC"}
@@ -18,6 +18,7 @@ export { ElementHighlight } from './components/ElementHighlight';
18
18
  export { EmptyState } from './components/EmptyState';
19
19
  export { GroupHeader } from './components/GroupHeader';
20
20
  export { TriggerJourney } from './components/TriggerJourney';
21
+ export { makeResizeHandles, PanelShellController } from './controllers/PanelShellController';
21
22
  export { formatConditionLabel } from './formatConditionLabel';
22
23
  export { useElementRect } from './hooks/useElementRect';
23
24
  export { useTriggerWhenStatus } from './hooks/useTriggerWhenStatus';
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * Does NOT pull in any React dependencies.
7
7
  */
8
+ import './components/AnchorPickerLit';
8
9
  import './components/EditorCardLit';
9
10
  import './components/EditorPanelShellLit';
10
11
  import './components/EditorTextareaLit';
@@ -1 +1 @@
1
- {"version":3,"file":"lit-elements.d.ts","sourceRoot":"","sources":["../src/lit-elements.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,4BAA4B,CAAC;AACpC,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AACxC,OAAO,kCAAkC,CAAC;AAC1C,OAAO,4BAA4B,CAAC;AACpC,OAAO,6BAA6B,CAAC"}
1
+ {"version":3,"file":"lit-elements.d.ts","sourceRoot":"","sources":["../src/lit-elements.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,8BAA8B,CAAC;AACtC,OAAO,4BAA4B,CAAC;AACpC,OAAO,kCAAkC,CAAC;AAC1C,OAAO,gCAAgC,CAAC;AACxC,OAAO,kCAAkC,CAAC;AAC1C,OAAO,4BAA4B,CAAC;AACpC,OAAO,6BAA6B,CAAC"}
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * Does NOT pull in any React dependencies.
7
7
  */
8
+ import './components/AnchorPickerLit';
8
9
  import './components/EditorCardLit';
9
10
  import './components/EditorPanelShellLit';
10
11
  import './components/EditorTextareaLit';
@@ -29,6 +29,7 @@
29
29
  "test:watch": "vitest"
30
30
  },
31
31
  "dependencies": {
32
+ "@syntro/design-system": "*",
32
33
  "css-selector-generator": "3.8.0",
33
34
  "lit": "3.3.2"
34
35
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syntrologie/adapt-content",
3
- "version": "2.8.0-canary.93",
3
+ "version": "2.8.0-canary.95",
4
4
  "description": "Adaptive Content app - DOM manipulation actions for text, attributes, and styles",
5
5
  "license": "Proprietary",
6
6
  "private": false,