@adea-ai/ui 0.103.1 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -122,6 +122,10 @@ host-specific permissions or metadata a shared section surface. Supply
122
122
  `resultsRegionLabel` and `detailRegionLabel` to name their keyboard-focusable,
123
123
  shared `ScrollArea` regions. The results area scrolls notices along with loading,
124
124
  empty, and error states even when those states contain no interactive control.
125
+ Use `CatalogDetail sectionsLayout="columns"` for related detail fields in a single
126
+ padded, outlined card. Its sections share dividers on wide screens and stack on
127
+ narrow screens; hosts should pass `CatalogDetailSection` children directly and
128
+ use no local padding or border overrides. The default keeps separate section cards.
125
129
 
126
130
  ```tsx
127
131
  import {
@@ -649,6 +653,12 @@ header content does not define the region's accessible name or the close
649
653
  button's name: both continue to come from `labelForLeaf`, so hosts should
650
654
  provide an accessible domain label there as well.
651
655
 
656
+ Pointer moves use a bounded, window-framed native drag-image snapshot while
657
+ the source dims and the handle changes from a grab to a grabbing cursor. The
658
+ temporary preview is inert, has no duplicate IDs, and is removed when dragging
659
+ ends. Reduced-motion preference disables the source opacity transition;
660
+ keyboard moves remain host-owned through `renderPaneActions`.
661
+
652
662
  The `HostPresentation` story shows a decorative icon and host title, keyboard
653
663
  pane stops, and a workspace-specific separator name. A narrow-width component
654
664
  fixture also checks that a long host title does not widen its pane header.
@@ -1,4 +1,5 @@
1
1
  import { type JSX } from 'solid-js';
2
+ type SectionsLayout = 'stacked' | 'columns';
2
3
  /** Shared item summary and action frame for catalog-owned detail content. */
3
4
  export declare function CatalogDetail(props: {
4
5
  title: string;
@@ -11,6 +12,8 @@ export declare function CatalogDetail(props: {
11
12
  badges?: JSX.Element;
12
13
  action?: JSX.Element;
13
14
  status?: JSX.Element;
15
+ /** Groups related fields in one padded card; stacks them on narrow screens. */
16
+ sectionsLayout?: SectionsLayout;
14
17
  children?: JSX.Element;
15
18
  }): JSX.Element;
16
19
  /** A shared compact row for host-specific fields, permissions, and copy. */
@@ -18,4 +21,5 @@ export declare function CatalogDetailSection(props: {
18
21
  title: string;
19
22
  children?: JSX.Element;
20
23
  }): JSX.Element;
24
+ export {};
21
25
  //# sourceMappingURL=catalog-detail.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog-detail.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAGzD,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/C,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,eA6BA;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CAAE,eAgBpF"}
1
+ {"version":3,"file":"catalog-detail.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAIpF,KAAK,cAAc,GAAG,SAAS,GAAG,SAAS,CAAA;AAqB3C,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAA;IAC/C,OAAO,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACvB,eAqCA;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CAAE,eAcpF"}
@@ -1,15 +1,26 @@
1
1
  import { Badge } from "../../ui/badge/badge.js";
2
- import { createComponent, insert, setAttribute, template } from "solid-js/web";
3
- import { Show, createUniqueId } from "solid-js";
2
+ import { className, createComponent, effect, insert, setAttribute, template } from "solid-js/web";
3
+ import { Show, createContext, createUniqueId, useContext } from "solid-js";
4
+ import { cva } from "class-variance-authority";
4
5
  //#region src/components/composites/catalog-browser/catalog-detail.tsx
5
6
  var _tmpl$ = /*#__PURE__*/ template(`<span class=shrink-0>`);
6
7
  var _tmpl$2 = /*#__PURE__*/ template(`<div class="flex shrink-0 flex-col items-end gap-2">`);
7
- var _tmpl$3 = /*#__PURE__*/ template(`<article class="grid min-w-0 gap-2"data-catalog-detail><div class="flex min-w-0 items-start gap-3 rounded-xl border border-border p-3"><div class="grid min-w-0 flex-1 gap-1"><div class="flex min-w-0 flex-wrap items-center gap-1.5"></div><h2 class="text-base leading-tight font-semibold tracking-tight"></h2><p class="text-muted-foreground text-sm"></p><p class="text-muted-foreground text-xs"></p></div></div><div class="grid min-w-0 gap-2">`);
8
- var _tmpl$4 = /*#__PURE__*/ template(`<section class="grid min-w-0 gap-1.5 rounded-xl border border-border p-3"><h3 class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"></h3><div class="grid min-w-0 gap-1.5">`);
8
+ var _tmpl$3 = /*#__PURE__*/ template(`<div data-catalog-detail-sections>`);
9
+ var _tmpl$4 = /*#__PURE__*/ template(`<article class="grid min-w-0 gap-2"data-catalog-detail><div class="flex min-w-0 items-start gap-3 rounded-xl border border-border p-3"><div class="grid min-w-0 flex-1 gap-1"><div class="flex min-w-0 flex-wrap items-center gap-1.5"></div><h2 class="text-base leading-tight font-semibold tracking-tight"></h2><p class="text-muted-foreground text-sm"></p><p class="text-muted-foreground text-xs">`);
10
+ var _tmpl$5 = /*#__PURE__*/ template(`<section><h3 class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"></h3><div class="grid min-w-0 gap-1.5">`);
11
+ var SectionsLayoutContext = createContext(() => "stacked");
12
+ var sectionsVariants = cva("grid min-w-0", { variants: { layout: {
13
+ stacked: "gap-2",
14
+ columns: "gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0"
15
+ } } });
16
+ var sectionVariants = cva("grid min-w-0 gap-1.5 wrap-anywhere", { variants: { layout: {
17
+ stacked: "rounded-xl border border-border p-3",
18
+ columns: "md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0"
19
+ } } });
9
20
  /** Shared item summary and action frame for catalog-owned detail content. */
10
21
  function CatalogDetail(props) {
11
22
  return (() => {
12
- var _el$ = _tmpl$3(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling, _el$0 = _el$2.nextSibling;
23
+ var _el$ = _tmpl$4(), _el$2 = _el$.firstChild, _el$4 = _el$2.firstChild, _el$5 = _el$4.firstChild, _el$6 = _el$5.nextSibling, _el$7 = _el$6.nextSibling, _el$8 = _el$7.nextSibling;
13
24
  insert(_el$2, createComponent(Show, {
14
25
  get when() {
15
26
  return props.leading;
@@ -54,19 +65,37 @@ function CatalogDetail(props) {
54
65
  return _el$9;
55
66
  }
56
67
  }), null);
57
- insert(_el$0, () => props.children);
68
+ insert(_el$, createComponent(SectionsLayoutContext.Provider, {
69
+ value: () => props.sectionsLayout ?? "stacked",
70
+ get children() {
71
+ var _el$0 = _tmpl$3();
72
+ insert(_el$0, () => props.children);
73
+ effect((_p$) => {
74
+ var _v$ = props.sectionsLayout ?? "stacked", _v$2 = sectionsVariants({ layout: props.sectionsLayout ?? "stacked" });
75
+ _v$ !== _p$.e && setAttribute(_el$0, "data-layout", _p$.e = _v$);
76
+ _v$2 !== _p$.t && className(_el$0, _p$.t = _v$2);
77
+ return _p$;
78
+ }, {
79
+ e: void 0,
80
+ t: void 0
81
+ });
82
+ return _el$0;
83
+ }
84
+ }), null);
58
85
  return _el$;
59
86
  })();
60
87
  }
61
88
  /** A shared compact row for host-specific fields, permissions, and copy. */
62
89
  function CatalogDetailSection(props) {
90
+ const layout = useContext(SectionsLayoutContext);
63
91
  const headingId = `catalog-detail-section-${createUniqueId()}`;
64
92
  return (() => {
65
- var _el$1 = _tmpl$4(), _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling;
93
+ var _el$1 = _tmpl$5(), _el$10 = _el$1.firstChild, _el$11 = _el$10.nextSibling;
66
94
  setAttribute(_el$1, "aria-labelledby", headingId);
67
95
  setAttribute(_el$10, "id", headingId);
68
96
  insert(_el$10, () => props.title);
69
97
  insert(_el$11, () => props.children);
98
+ effect(() => className(_el$1, sectionVariants({ layout: layout() })));
70
99
  return _el$1;
71
100
  })();
72
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"catalog-detail.js","names":["createUniqueId","Show","JSX","Badge","CatalogDetail","props","title","description","category","publisher","publishedByLabel","leading","Element","eyebrow","badges","action","status","children","_el$","_tmpl$3","_el$2","firstChild","_el$4","_el$5","_el$6","nextSibling","_el$7","_el$8","_el$0","_$insert","_$createComponent","when","_el$3","_tmpl$","variant","_el$9","_tmpl$2","CatalogDetailSection","headingId","_el$1","_tmpl$4","_el$10","_el$11","_$setAttribute","_$createComponent"],"sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"sourcesContent":["import { createUniqueId, Show, type JSX } from 'solid-js'\nimport { Badge } from '../../ui/badge'\n\n/** Shared item summary and action frame for catalog-owned detail content. */\nexport function CatalogDetail(props: {\n title: string\n description: string\n category: string\n publisher: string\n publishedByLabel: (publisher: string) => string\n leading?: JSX.Element\n eyebrow?: string\n badges?: JSX.Element\n action?: JSX.Element\n status?: JSX.Element\n children?: JSX.Element\n}) {\n return (\n <article class=\"grid min-w-0 gap-2\" data-catalog-detail>\n <div class=\"flex min-w-0 items-start gap-3 rounded-xl border border-border p-3\">\n <Show when={props.leading}>\n <span class=\"shrink-0\">{props.leading}</span>\n </Show>\n <div class=\"grid min-w-0 flex-1 gap-1\">\n <div class=\"flex min-w-0 flex-wrap items-center gap-1.5\">\n <Show when={props.eyebrow}>\n <Badge variant=\"outline\">{props.eyebrow}</Badge>\n </Show>\n <Badge variant=\"secondary\">{props.category}</Badge>\n {props.badges}\n </div>\n <h2 class=\"text-base leading-tight font-semibold tracking-tight\">{props.title}</h2>\n <p class=\"text-muted-foreground text-sm\">{props.description}</p>\n <p class=\"text-muted-foreground text-xs\">{props.publishedByLabel(props.publisher)}</p>\n </div>\n <Show when={props.action || props.status}>\n <div class=\"flex shrink-0 flex-col items-end gap-2\">\n {props.action}\n {props.status}\n </div>\n </Show>\n </div>\n <div class=\"grid min-w-0 gap-2\">{props.children}</div>\n </article>\n )\n}\n\n/** A shared compact row for host-specific fields, permissions, and copy. */\nexport function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {\n const headingId = `catalog-detail-section-${createUniqueId()}`\n return (\n <section\n class=\"grid min-w-0 gap-1.5 rounded-xl border border-border p-3\"\n aria-labelledby={headingId}\n >\n <h3\n id={headingId}\n class=\"text-2xs font-semibold tracking-wider text-muted-foreground uppercase\"\n >\n {props.title}\n </h3>\n <div class=\"grid min-w-0 gap-1.5\">{props.children}</div>\n </section>\n )\n}\n"],"mappings":";;;;;;;;;AAIA,SAAgBI,cAAcC,OAY3B;CACD,cAAA;EAAA,IAAAa,OAAAC,QAAA,GAAAC,QAAAF,KAAAG,YAAAC,QAAAF,MAAAC,YAAAE,QAAAD,MAAAD,YAAAG,QAAAD,MAAAE,aAAAC,QAAAF,MAAAC,aAAAE,QAAAD,MAAAD,aAAAG,QAAAR,MAAAK;EAAAI,OAAAT,OAAAU,gBAGO7B,MAAI;GAAA,IAAC8B,OAAI;IAAA,OAAE1B,MAAMM;GAAO;GAAA,IAAAM,WAAA;IAAA,IAAAe,QAAAC,OAAA;IAAAJ,OAAAG,aACC3B,MAAMM,OAAO;IAAA,OAAAqB;GAAA;EAAA,CAAA,GAAAV,KAAA;EAAAO,OAAAN,OAAAO,gBAIlC7B,MAAI;GAAA,IAAC8B,OAAI;IAAA,OAAE1B,MAAMQ;GAAO;GAAA,IAAAI,WAAA;IAAA,OAAAa,gBACtB3B,OAAK;KAAC+B,SAAO;KAAA,IAAAjB,WAAA;MAAA,OAAYZ,MAAMQ;KAAO;IAAA,CAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAgB,OAAAN,OAAAO,gBAExC3B,OAAK;GAAC+B,SAAO;GAAA,IAAAjB,WAAA;IAAA,OAAcZ,MAAMG;GAAQ;EAAA,CAAA,GAAA,IAAA;EAAAqB,OAAAN,aACzClB,MAAMS,QAAM,IAAA;EAAAe,OAAAL,aAEmDnB,MAAMC,KAAK;EAAAuB,OAAAH,aACnCrB,MAAME,WAAW;EAAAsB,OAAAF,aACjBtB,MAAMK,iBAAiBL,MAAMI,SAAS,CAAC;EAAAoB,OAAAT,OAAAU,gBAElF7B,MAAI;GAAA,IAAC8B,OAAI;IAAA,OAAE1B,MAAMU,UAAUV,MAAMW;GAAM;GAAA,IAAAC,WAAA;IAAA,IAAAkB,QAAAC,QAAA;IAAAP,OAAAM,aAEnC9B,MAAMU,QAAM,IAAA;IAAAc,OAAAM,aACZ9B,MAAMW,QAAM,IAAA;IAAA,OAAAmB;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAN,OAAAD,aAIcvB,MAAMY,QAAQ;EAAA,OAAAC;CAAA,EAAA,CAAA;AAGrD;;AAGA,SAAgBmB,qBAAqBhC,OAAkD;CACrF,MAAMiC,YAAY,0BAA0BtC,eAAe;CAC3D,cAAA;EAAA,IAAAuC,QAAAC,QAAA,GAAAC,SAAAF,MAAAlB,YAAAqB,SAAAD,OAAAhB;EAAAkB,aAAAJ,OAAA,mBAGqBD,SAAS;EAAAK,aAAAF,QAAA,MAGpBH,SAAS;EAAAT,OAAAY,cAGZpC,MAAMC,KAAK;EAAAuB,OAAAa,cAEqBrC,MAAMY,QAAQ;EAAA,OAAAsB;CAAA,EAAA,CAAA;AAGvD"}
1
+ {"version":3,"file":"catalog-detail.js","names":["createContext","createUniqueId","Show","useContext","JSX","Badge","cva","SectionsLayout","SectionsLayoutContext","sectionsVariants","variants","layout","stacked","columns","sectionVariants","CatalogDetail","props","title","description","category","publisher","publishedByLabel","leading","Element","eyebrow","badges","action","status","sectionsLayout","children","_el$","_tmpl$4","_el$2","firstChild","_el$4","_el$5","_el$6","nextSibling","_el$7","_el$8","_$insert","_$createComponent","when","_el$3","_tmpl$","variant","_el$9","_tmpl$2","Provider","value","_el$0","_tmpl$3","_$effect","_p$","_v$","_v$2","e","_$setAttribute","t","_$className","undefined","CatalogDetailSection","headingId","_el$1","_tmpl$5","_el$10","_el$11","_$createComponent","_$setAttribute","_$className"],"sources":["../../../../src/components/composites/catalog-browser/catalog-detail.tsx"],"sourcesContent":["import { createContext, createUniqueId, Show, useContext, type JSX } from 'solid-js'\nimport { Badge } from '../../ui/badge'\nimport { cva } from 'class-variance-authority'\n\ntype SectionsLayout = 'stacked' | 'columns'\nconst SectionsLayoutContext = createContext<() => SectionsLayout>((): SectionsLayout => 'stacked')\nconst sectionsVariants = cva('grid min-w-0', {\n variants: {\n layout: {\n stacked: 'gap-2',\n columns:\n 'gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0',\n },\n },\n})\nconst sectionVariants = cva('grid min-w-0 gap-1.5 wrap-anywhere', {\n variants: {\n layout: {\n stacked: 'rounded-xl border border-border p-3',\n columns:\n 'md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0',\n },\n },\n})\n\n/** Shared item summary and action frame for catalog-owned detail content. */\nexport function CatalogDetail(props: {\n title: string\n description: string\n category: string\n publisher: string\n publishedByLabel: (publisher: string) => string\n leading?: JSX.Element\n eyebrow?: string\n badges?: JSX.Element\n action?: JSX.Element\n status?: JSX.Element\n /** Groups related fields in one padded card; stacks them on narrow screens. */\n sectionsLayout?: SectionsLayout\n children?: JSX.Element\n}) {\n return (\n <article class=\"grid min-w-0 gap-2\" data-catalog-detail>\n <div class=\"flex min-w-0 items-start gap-3 rounded-xl border border-border p-3\">\n <Show when={props.leading}>\n <span class=\"shrink-0\">{props.leading}</span>\n </Show>\n <div class=\"grid min-w-0 flex-1 gap-1\">\n <div class=\"flex min-w-0 flex-wrap items-center gap-1.5\">\n <Show when={props.eyebrow}>\n <Badge variant=\"outline\">{props.eyebrow}</Badge>\n </Show>\n <Badge variant=\"secondary\">{props.category}</Badge>\n {props.badges}\n </div>\n <h2 class=\"text-base leading-tight font-semibold tracking-tight\">{props.title}</h2>\n <p class=\"text-muted-foreground text-sm\">{props.description}</p>\n <p class=\"text-muted-foreground text-xs\">{props.publishedByLabel(props.publisher)}</p>\n </div>\n <Show when={props.action || props.status}>\n <div class=\"flex shrink-0 flex-col items-end gap-2\">\n {props.action}\n {props.status}\n </div>\n </Show>\n </div>\n <SectionsLayoutContext.Provider value={() => props.sectionsLayout ?? 'stacked'}>\n <div\n data-catalog-detail-sections\n data-layout={props.sectionsLayout ?? 'stacked'}\n class={sectionsVariants({ layout: props.sectionsLayout ?? 'stacked' })}\n >\n {props.children}\n </div>\n </SectionsLayoutContext.Provider>\n </article>\n )\n}\n\n/** A shared compact row for host-specific fields, permissions, and copy. */\nexport function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {\n const layout = useContext(SectionsLayoutContext)\n const headingId = `catalog-detail-section-${createUniqueId()}`\n return (\n <section class={sectionVariants({ layout: layout() })} aria-labelledby={headingId}>\n <h3\n id={headingId}\n class=\"text-2xs font-semibold tracking-wider text-muted-foreground uppercase\"\n >\n {props.title}\n </h3>\n <div class=\"grid min-w-0 gap-1.5\">{props.children}</div>\n </section>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"split-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAE5C,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAA;IACjC,yFAAyF;IACzF,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IAC9C,yFAAyF;IACzF,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACpD,+GAA+G;IAC/G,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IACrB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAA;IAC5D,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAChD,qGAAqG;IACrG,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3E,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AASD,mGAAmG;AACnG,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,eAuThF"}
1
+ {"version":3,"file":"split-layout.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"names":[],"mappings":"AACA,OAAO,EAQL,KAAK,QAAQ,EACb,KAAK,GAAG,EACT,MAAM,UAAU,CAAA;AAKjB,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,QAAQ,CAAA;AAC5D,YAAY,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAI5C,OAAO,EAIL,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,SAAS,CAAA;AAEhB,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,eAAe,IAAI;IACxD,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAA;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,YAAY,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAA;IACjC,yFAAyF;IACzF,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IAC9C,yFAAyF;IACzF,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACpD,+GAA+G;IAC/G,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAA;IACrB,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,MAAM,CAAA;IAC5D,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IAChD,qGAAqG;IACrG,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3E,qFAAqF;IACrF,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,OAAO,CAAA;IACtD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAqHD,mGAAmG;AACnG,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,eAqVhF"}
@@ -9,9 +9,11 @@ import { For, createComputed, createMemo, createSignal, createUniqueId, on, onCl
9
9
  import Resizable from "@corvu/resizable";
10
10
  //#region src/components/layout/split-layout/split-layout.tsx
11
11
  var _tmpl$ = /*#__PURE__*/ template(`<div role=group data-slot=split-layout><span class=sr-only role=status>`);
12
- var _tmpl$2 = /*#__PURE__*/ template(`<section role=region class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"><header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2"><span data-pane-drag-handle><span class="min-w-0 flex-1 truncate"></span></span></header><div class="min-h-0 min-w-0 flex-1 overflow-auto">`);
12
+ var _tmpl$2 = /*#__PURE__*/ template(`<section role=region class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"><header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2"><span data-pane-drag-handle><span class="min-w-0 flex-1 truncate"></span></span></header><div class="min-h-0 min-w-0 flex-1 overflow-auto">`);
13
13
  var _tmpl$3 = /*#__PURE__*/ template(`<div aria-hidden=true>`);
14
14
  var PANE_DRAG_TYPE = "application/x-adea-pane-move";
15
+ var DRAG_PREVIEW_MAX_WIDTH = 384;
16
+ var DRAG_PREVIEW_MAX_HEIGHT = 256;
15
17
  function rectStyle(rect) {
16
18
  return {
17
19
  left: `${rect.x * 100}%`,
@@ -20,6 +22,99 @@ function rectStyle(rect) {
20
22
  height: `${rect.height * 100}%`
21
23
  };
22
24
  }
25
+ function dragPreviewPlaceholder(source, message) {
26
+ const placeholder = document.createElement("div");
27
+ placeholder.className = cn(source.getAttribute("class"), "flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground");
28
+ placeholder.setAttribute("data-split-drag-placeholder", source.localName);
29
+ placeholder.textContent = message;
30
+ return placeholder;
31
+ }
32
+ function boundedSnapshotSize(width, height) {
33
+ const sourceWidth = Math.max(1, width);
34
+ const sourceHeight = Math.max(1, height);
35
+ const scale = Math.min(1, DRAG_PREVIEW_MAX_WIDTH / sourceWidth, DRAG_PREVIEW_MAX_HEIGHT / sourceHeight);
36
+ return {
37
+ width: Math.max(1, Math.round(sourceWidth * scale)),
38
+ height: Math.max(1, Math.round(sourceHeight * scale))
39
+ };
40
+ }
41
+ function cloneDragPreviewNode(source) {
42
+ if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false);
43
+ if (!(source instanceof HTMLElement || source instanceof SVGElement)) return source.cloneNode(false);
44
+ if ([
45
+ "script",
46
+ "style",
47
+ "link",
48
+ "source",
49
+ "track",
50
+ "meta"
51
+ ].includes(source.localName)) return void 0;
52
+ if ([
53
+ "iframe",
54
+ "video",
55
+ "audio",
56
+ "object",
57
+ "embed"
58
+ ].includes(source.localName)) return dragPreviewPlaceholder(source, "Embedded content unavailable while moving");
59
+ if (source instanceof HTMLImageElement) {
60
+ const snapshot = document.createElement("canvas");
61
+ const size = boundedSnapshotSize(source.naturalWidth || source.width, source.naturalHeight || source.height);
62
+ snapshot.width = size.width;
63
+ snapshot.height = size.height;
64
+ snapshot.className = source.getAttribute("class") ?? "";
65
+ snapshot.setAttribute("data-split-drag-image", "");
66
+ try {
67
+ if (!source.complete || source.naturalWidth === 0) throw new Error("image has no decoded pixels");
68
+ const context = snapshot.getContext("2d");
69
+ if (!context) throw new Error("canvas snapshot context is unavailable");
70
+ context.drawImage(source, 0, 0, snapshot.width, snapshot.height);
71
+ return snapshot;
72
+ } catch {
73
+ return dragPreviewPlaceholder(source, "Image preview unavailable");
74
+ }
75
+ }
76
+ if (source instanceof HTMLElement && source.localName.includes("-")) {
77
+ const children = document.createDocumentFragment();
78
+ for (const child of source.childNodes) {
79
+ const clone = cloneDragPreviewNode(child);
80
+ if (clone) children.append(clone);
81
+ }
82
+ return children.childNodes.length > 0 ? children : dragPreviewPlaceholder(source, "Live component preview unavailable while moving");
83
+ }
84
+ const clone = source.cloneNode(false);
85
+ for (const attribute of [
86
+ "id",
87
+ "style",
88
+ "draggable",
89
+ "data-pane-drag-handle",
90
+ "data-pane-id",
91
+ "src",
92
+ "srcset",
93
+ "href",
94
+ "xlink:href",
95
+ "data",
96
+ "poster"
97
+ ]) clone.removeAttribute(attribute);
98
+ for (const attribute of Array.from(clone.attributes)) if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name);
99
+ if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {
100
+ const size = boundedSnapshotSize(source.width, source.height);
101
+ clone.width = size.width;
102
+ clone.height = size.height;
103
+ try {
104
+ const context = clone.getContext("2d");
105
+ if (!context) throw new Error("canvas snapshot context is unavailable");
106
+ context.drawImage(source, 0, 0, clone.width, clone.height);
107
+ } catch {
108
+ return dragPreviewPlaceholder(source, "Canvas preview unavailable while moving");
109
+ }
110
+ return clone;
111
+ }
112
+ for (const child of source.childNodes) {
113
+ const childClone = cloneDragPreviewNode(child);
114
+ if (childClone) clone.append(childClone);
115
+ }
116
+ return clone;
117
+ }
23
118
  /** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */
24
119
  function SplitLayout(props) {
25
120
  let root;
@@ -29,11 +124,34 @@ function SplitLayout(props) {
29
124
  const prefix = createUniqueId();
30
125
  let nextDomId = 0;
31
126
  let nextDragId = 0;
127
+ let dragPreview;
32
128
  const [drag, setDrag] = createSignal();
33
129
  const [drop, setDrop] = createSignal();
34
130
  const clearDrag = () => {
35
131
  setDrag(void 0);
36
132
  setDrop(void 0);
133
+ dragPreview?.remove();
134
+ dragPreview = void 0;
135
+ };
136
+ const createDragPreview = (source) => {
137
+ dragPreview?.remove();
138
+ const preview = cloneDragPreviewNode(source);
139
+ preview.className = "pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl";
140
+ preview.removeAttribute("id");
141
+ preview.removeAttribute("style");
142
+ preview.removeAttribute("role");
143
+ preview.removeAttribute("aria-label");
144
+ preview.removeAttribute("data-pane-id");
145
+ preview.removeAttribute("data-focused");
146
+ preview.removeAttribute("data-drag-source");
147
+ preview.removeAttribute("data-drop-direction");
148
+ preview.removeAttribute("data-drop-placement");
149
+ preview.setAttribute("data-split-drag-preview", "");
150
+ preview.setAttribute("aria-hidden", "true");
151
+ preview.inert = true;
152
+ document.body.append(preview);
153
+ dragPreview = preview;
154
+ return preview;
37
155
  };
38
156
  const domId = (id) => {
39
157
  const existing = domIds.get(id);
@@ -163,7 +281,14 @@ function SplitLayout(props) {
163
281
  event.dataTransfer.effectAllowed = "move";
164
282
  const source = refs.get(id);
165
283
  const frame = source?.getBoundingClientRect();
166
- if (source && frame && typeof event.dataTransfer.setDragImage === "function") event.dataTransfer.setDragImage(source, Math.max(0, Math.round(event.clientX - frame.left)), Math.max(0, Math.round(event.clientY - frame.top)));
284
+ if (source && frame && typeof event.dataTransfer.setDragImage === "function") {
285
+ const preview = createDragPreview(source);
286
+ const previewFrame = preview.getBoundingClientRect();
287
+ const scaleX = previewFrame.width / frame.width;
288
+ const scaleY = previewFrame.height / frame.height;
289
+ event.dataTransfer.setDragImage(preview, Math.max(0, Math.round((event.clientX - frame.left) * scaleX)), Math.max(0, Math.round((event.clientY - frame.top) * scaleY)));
290
+ preview.setAttribute("data-native-drag-image", "");
291
+ }
167
292
  });
168
293
  insert(_el$5, (() => {
169
294
  var _c$ = memo(() => !!props.onMove);
@@ -208,7 +333,10 @@ function SplitLayout(props) {
208
333
  y: 0,
209
334
  width: 0,
210
335
  height: 0
211
- }), _v$10 = Boolean(props.onMove), _v$11 = cn("flex min-w-0 flex-1 items-center gap-2 truncate text-sm", { "cursor-grab": Boolean(props.onMove) }), _v$12 = props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : void 0;
336
+ }), _v$10 = Boolean(props.onMove), _v$11 = cn("flex min-w-0 flex-1 items-center gap-2 truncate text-sm", {
337
+ "cursor-grab": Boolean(props.onMove),
338
+ "cursor-grabbing": drag()?.id === id
339
+ }), _v$12 = props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : void 0;
212
340
  _v$4 !== _p$.e && setAttribute(_el$3, "id", _p$.e = _v$4);
213
341
  _v$5 !== _p$.t && setAttribute(_el$3, "aria-label", _p$.t = _v$5);
214
342
  _v$6 !== _p$.a && setAttribute(_el$3, "tabindex", _p$.a = _v$6);
@@ -1 +1 @@
1
- {"version":3,"file":"split-layout.js","names":["Resizable","For","createMemo","createComputed","createUniqueId","createSignal","onCleanup","on","Accessor","JSX","X","GripVertical","Button","cn","computeLayoutFrames","LayoutRect","paneDropIntent","PaneDropIntent","PANE_DRAG_TYPE","listLeaves","MIN_SPLIT_RATIO","MAX_SPLIT_RATIO","SplitLayoutBranch","SplitLayoutLeaf","SplitLayoutState","SplitLayoutProps","state","L","label","labelForLeaf","leaf","renderLeaf","Element","renderPaneLabel","paneTabIndex","labelForSeparator","branch","onResize","splitId","ratio","onFocus","leafId","onClose","onMove","targetId","intent","renderPaneActions","class","rectStyle","rect","CSSProperties","left","x","top","y","width","height","SplitLayout","props","root","HTMLDivElement","pendingFrame","refs","Map","HTMLElement","domIds","prefix","nextDomId","nextDragId","drag","setDrag","id","token","drop","setDrop","clearDrag","undefined","domId","existing","get","created","set","leaves","center","leafMap","map","frames","branches","Array","from","values","flatMap","frame","node","kind","current","has","target","allowedTarget","transfer","DataTransfer","types","includes","dropLabel","placement","branchMap","schedule","action","cancelAnimationFrame","requestAnimationFrame","active","ownerDocument","activeElement","contains","field","HTMLTextAreaElement","HTMLInputElement","selection","start","selectionStart","end","selectionEnd","direction","selectionDirection","isConnected","body","focus","preventScroll","setSelectionRange","clear","close","next","_el$","_tmpl$","_el$2","firstChild","_$use","el","_$insert","_$createComponent","each","children","initial","content","paneLabel","actions","delete","_el$3","_tmpl$2","_el$4","_el$5","_el$6","_el$7","nextSibling","$$focusin","addEventListener","event","dataTransfer","clientX","clientY","currentTarget","getBoundingClientRect","accepted","getData","preventDefault","relatedTarget","Node","dropEffect","_$setAttribute","setData","effectAllowed","source","setDragImage","Math","max","round","_c$","_$memo","_c$2","variant","size","aria-label","onClick","_c$3","_el$8","_tmpl$3","_$effect","_$className","_p$","_v$4","_v$5","_v$6","_v$7","focusedLeafId","_v$8","_v$9","_v$0","_v$1","_v$10","Boolean","_v$11","_v$12","e","t","a","o","i","n","s","h","_$style","r","d","l","orientation","sizes","keyboardDelta","onSizesChange","getClientRects","length","some","Number","isFinite","abs","style","Panel","minSize","maxSize","Handle","aria-orientation","aria-controls","join","aria-valuenow","_v$","_v$2","_v$3","_$delegateEvents","_$createComponent","_$memo","_$className","_$setAttribute","_$style","_$delegateEvents"],"sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"sourcesContent":["import Resizable from '@corvu/resizable'\nimport {\n For,\n createMemo,\n createComputed,\n createUniqueId,\n createSignal,\n onCleanup,\n on,\n type Accessor,\n type JSX,\n} from 'solid-js'\nimport { X, GripVertical } from 'lucide-solid'\nimport { Button } from '../../ui/button'\nimport { cn } from '#lib/utils'\nimport { computeLayoutFrames, type LayoutRect } from './geometry'\nimport { paneDropIntent, type PaneDropIntent } from './drop'\nexport type { PaneDropIntent } from './drop'\nconst PANE_DRAG_TYPE = 'application/x-adea-pane-move'\nimport {\n listLeaves,\n MIN_SPLIT_RATIO,\n MAX_SPLIT_RATIO,\n type SplitLayoutBranch,\n type SplitLayoutLeaf,\n type SplitLayoutState,\n} from './model'\n\nexport type SplitLayoutProps<L extends SplitLayoutLeaf> = {\n state: SplitLayoutState<L>\n label: string\n /** Accessible name for a pane region and its close button. */\n labelForLeaf: (leaf: L) => string\n /** Called once per stable leaf owner; the accessor tracks opaque payload replacement. */\n renderLeaf: (leaf: Accessor<L>) => JSX.Element\n /** Inline visible header composition, separate from the region and close-button name. */\n renderPaneLabel?: (leaf: Accessor<L>) => JSX.Element\n /** Whether pane regions participate in sequential keyboard navigation; defaults to programmatic focus only. */\n paneTabIndex?: 0 | -1\n /** Accessible splitter name; defaults to the current orientation-specific label. */\n labelForSeparator?: (branch: SplitLayoutBranch<L>) => string\n onResize: (splitId: string, ratio: number) => void\n onFocus?: (leafId: string) => void\n /** Host performs the model transition and returns the surviving focus destination. */\n onClose?: (leafId: string) => string | undefined\n /** Pointer placement only; the host supplies keyboard commands and performs the model transition. */\n onMove?: (leafId: string, targetId: string, intent: PaneDropIntent) => void\n /** Stable header composition for host-owned keyboard actions/capability feedback. */\n renderPaneActions?: (leaf: Accessor<L>) => JSX.Element\n class?: string\n}\nfunction rectStyle(rect: LayoutRect): JSX.CSSProperties {\n return {\n left: `${rect.x * 100}%`,\n top: `${rect.y * 100}%`,\n width: `${rect.width * 100}%`,\n height: `${rect.height * 100}%`,\n }\n}\n/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */\nexport function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {\n let root: HTMLDivElement | undefined\n let pendingFrame: number | undefined\n const refs = new Map<string, HTMLElement>()\n const domIds = new Map<string, string>()\n const prefix = createUniqueId()\n let nextDomId = 0\n let nextDragId = 0\n const [drag, setDrag] = createSignal<{ id: string; token: string }>()\n const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()\n const clearDrag = () => {\n setDrag(undefined)\n setDrop(undefined)\n }\n const domId = (id: string) => {\n const existing = domIds.get(id)\n if (existing) return existing\n const created = `${prefix}-pane-${nextDomId++}`\n domIds.set(id, created)\n return created\n }\n const leaves = createMemo(() => listLeaves(props.state.center))\n const leafMap = createMemo(() => new Map(leaves().map((leaf) => [leaf.id, leaf])))\n const frames = createMemo(() => computeLayoutFrames(props.state.center))\n const branches = createMemo(() =>\n Array.from(frames().values()).flatMap((frame) =>\n frame.node.kind === 'split' ? [frame.node] : []\n )\n )\n createComputed(() => {\n const current = drag()\n if (current && (!props.onMove || !leafMap().has(current.id))) clearDrag()\n const target = drop()\n if (target && !leafMap().has(target.id)) setDrop(undefined)\n })\n const allowedTarget = (id: string, transfer: DataTransfer | null) => {\n const current = drag()\n return props.onMove &&\n current &&\n current.id !== id &&\n leafMap().has(current.id) &&\n leafMap().has(id) &&\n transfer?.types.includes(PANE_DRAG_TYPE)\n ? current\n : undefined\n }\n const dropLabel = createMemo(() => {\n const target = drop()\n const leaf = target ? leafMap().get(target.id) : undefined\n return target && leaf\n ? `Drop to place pane ${target.intent.placement} ${props.labelForLeaf(leaf)}`\n : ''\n })\n const branchMap = createMemo(() => new Map(branches().map((branch) => [branch.id, branch])))\n const schedule = (action: () => void) => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n pendingFrame = requestAnimationFrame(() => {\n pendingFrame = undefined\n action()\n })\n }\n // Register before keyed children update: capture a focused editor before an\n // ordered DOM move can blur it. Never override a newer focus outside this root.\n createComputed(\n on(\n () => props.state.center,\n () => {\n const active = root?.ownerDocument.activeElement\n if (!root || !(active instanceof HTMLElement) || !root.contains(active)) return\n const field =\n active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement\n ? active\n : undefined\n const selection = field\n ? {\n start: field.selectionStart,\n end: field.selectionEnd,\n direction: field.selectionDirection,\n }\n : undefined\n schedule(() => {\n if (!active.isConnected || !root?.contains(active)) return\n const current = active.ownerDocument.activeElement\n if (current !== active && current !== active.ownerDocument.body) return\n active.focus({ preventScroll: true })\n if (field && selection?.start !== null && selection?.end !== null && selection)\n field.setSelectionRange(\n selection.start,\n selection.end,\n selection.direction ?? undefined\n )\n })\n }\n )\n )\n onCleanup(() => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n clearDrag()\n refs.clear()\n domIds.clear()\n })\n const close = (id: string) => {\n const next = props.onClose?.(id)\n if (next) schedule(() => refs.get(next)?.focus({ preventScroll: true }))\n }\n return (\n <div\n ref={(el) => {\n root = el\n }}\n role=\"group\"\n aria-label={props.label}\n data-slot=\"split-layout\"\n data-dragging={drag() ? '' : undefined}\n class={cn('relative size-full min-h-0 min-w-0 data-[dragging]:cursor-grabbing', props.class)}\n >\n <For each={leaves().map((leaf) => leaf.id)}>\n {(id) => {\n const initial = leafMap().get(id)!\n const leaf = () => leafMap().get(id) ?? initial\n const content = props.renderLeaf(leaf)\n const paneLabel = props.renderPaneLabel?.(leaf)\n const actions = props.renderPaneActions?.(leaf)\n const intent = () => (drop()?.id === id ? drop()?.intent : undefined)\n onCleanup(() => {\n refs.delete(id)\n domIds.delete(id)\n })\n return (\n <section\n ref={(el) => refs.set(id, el)}\n id={domId(id)}\n role=\"region\"\n aria-label={props.labelForLeaf(leaf())}\n tabIndex={props.paneTabIndex ?? -1}\n data-pane-id={id}\n data-focused={props.state.focusedLeafId === id ? '' : undefined}\n data-drag-source={drag()?.id === id ? '' : undefined}\n data-drop-direction={intent()?.direction}\n data-drop-placement={intent()?.placement}\n onDragOver={(event) => {\n if (!allowedTarget(id, event.dataTransfer)) return\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n if (!next) {\n setDrop(undefined)\n return\n }\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n setDrop({ id, intent: next })\n }}\n onDragLeave={(event) => {\n if (\n event.relatedTarget instanceof Node &&\n event.currentTarget.contains(event.relatedTarget)\n )\n return\n if (drop()?.id === id) setDrop(undefined)\n }}\n onDrop={(event) => {\n const current = allowedTarget(id, event.dataTransfer)\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n const accepted =\n current && next && event.dataTransfer?.getData(PANE_DRAG_TYPE) === current.token\n clearDrag()\n if (!accepted) return\n event.preventDefault()\n props.onMove?.(current.id, id, next)\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring\"\n onFocusIn={() => props.onFocus?.(id)}\n >\n <header class=\"flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2\">\n <span\n data-pane-drag-handle=\"\"\n draggable={Boolean(props.onMove)}\n class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {\n 'cursor-grab': Boolean(props.onMove),\n })}\n title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}\n onDragStart={(event) => {\n if (!props.onMove || !event.dataTransfer) {\n event.preventDefault()\n return\n }\n const token = `${prefix}-${++nextDragId}`\n setDrag({ id, token })\n event.dataTransfer.setData(PANE_DRAG_TYPE, token)\n event.dataTransfer.effectAllowed = 'move'\n // The ghost is the pane itself, not the handle strip: the\n // browser snapshots the region before the drag state dims\n // this section, so the dragged pane stays readable while\n // its source visibly recedes.\n const source = refs.get(id)\n const frame = source?.getBoundingClientRect()\n if (source && frame && typeof event.dataTransfer.setDragImage === 'function')\n event.dataTransfer.setDragImage(\n source,\n Math.max(0, Math.round(event.clientX - frame.left)),\n Math.max(0, Math.round(event.clientY - frame.top))\n )\n }}\n onDragEnd={clearDrag}\n >\n {props.onMove ? (\n <GripVertical class=\"size-3 shrink-0\" aria-hidden=\"true\" />\n ) : null}\n <span class=\"min-w-0 flex-1 truncate\">\n {paneLabel ?? props.labelForLeaf(leaf())}\n </span>\n </span>\n {actions}\n {props.onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={`Close ${props.labelForLeaf(leaf())}`}\n onClick={() => close(id)}\n >\n <X />\n </Button>\n ) : null}\n </header>\n <div class=\"min-h-0 min-w-0 flex-1 overflow-auto\">{content}</div>\n {intent() ? (\n <div\n aria-hidden=\"true\"\n class={cn('pointer-events-none absolute border-2 border-primary bg-primary/10', {\n 'inset-y-0 left-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'before',\n 'inset-y-0 right-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'after',\n 'inset-x-0 top-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'before',\n 'inset-x-0 bottom-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'after',\n })}\n />\n ) : null}\n </section>\n )\n }}\n </For>\n <span class=\"sr-only\" role=\"status\">\n {dropLabel()}\n </span>\n <For each={branches().map((branch) => branch.id)}>\n {(id) => {\n const initial = branchMap().get(id)!\n const branch: Accessor<SplitLayoutBranch<L>> = () => branchMap().get(id) ?? initial\n return (\n <Resizable\n orientation={branch().direction === 'row' ? 'horizontal' : 'vertical'}\n sizes={[branch().ratio, 1 - branch().ratio]}\n keyboardDelta={0.05}\n onSizesChange={(sizes) => {\n // Corvu also reports panel registration/unregistration while\n // mounting or disposing a controller. Those incomplete or\n // hidden-host sizes are not user resize intent.\n if (!root?.isConnected || root.getClientRects().length === 0) return\n if (\n sizes.length !== 2 ||\n sizes.some((size) => !Number.isFinite(size)) ||\n Math.abs(sizes[0]! + sizes[1]! - 1) > 0.000001\n )\n return\n if (sizes[0] !== undefined && Math.abs(sizes[0] - branch().ratio) > 0.000001)\n props.onResize(id, sizes[0])\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"pointer-events-none absolute flex min-h-0 min-w-0 data-[orientation=vertical]:flex-col\"\n >\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n <Resizable.Handle\n aria-label={\n props.labelForSeparator?.(branch()) ??\n (branch().direction === 'row' ? 'Resize pane columns' : 'Resize pane rows')\n }\n aria-orientation={branch().direction === 'row' ? 'vertical' : 'horizontal'}\n aria-controls={listLeaves(branch())\n .map((leaf) => domId(leaf.id))\n .join(' ')}\n aria-valuemin={10}\n aria-valuemax={90}\n aria-valuenow={Math.round(branch().ratio * 100)}\n class=\"pointer-events-auto relative w-px shrink-0 bg-border after:absolute after:inset-y-0 after:-inset-x-1 after:w-3 focus-visible:bg-primary focus-visible:outline-none hover:bg-primary data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:inset-x-0 data-[orientation=vertical]:after:-inset-y-1 data-[orientation=vertical]:after:h-3 data-[orientation=vertical]:after:w-full\"\n />\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n </Resizable>\n )\n }}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"split-layout.js","names":["Resizable","For","createMemo","createComputed","createUniqueId","createSignal","onCleanup","on","Accessor","JSX","X","GripVertical","Button","cn","computeLayoutFrames","LayoutRect","paneDropIntent","PaneDropIntent","PANE_DRAG_TYPE","DRAG_PREVIEW_MAX_WIDTH","DRAG_PREVIEW_MAX_HEIGHT","listLeaves","MIN_SPLIT_RATIO","MAX_SPLIT_RATIO","SplitLayoutBranch","SplitLayoutLeaf","SplitLayoutState","SplitLayoutProps","state","L","label","labelForLeaf","leaf","renderLeaf","Element","renderPaneLabel","paneTabIndex","labelForSeparator","branch","onResize","splitId","ratio","onFocus","leafId","onClose","onMove","targetId","intent","renderPaneActions","class","rectStyle","rect","CSSProperties","left","x","top","y","width","height","dragPreviewPlaceholder","source","HTMLElement","message","placeholder","document","createElement","className","getAttribute","setAttribute","localName","textContent","boundedSnapshotSize","sourceWidth","Math","max","sourceHeight","scale","min","round","cloneDragPreviewNode","Node","nodeType","TEXT_NODE","cloneNode","SVGElement","includes","undefined","HTMLImageElement","snapshot","size","naturalWidth","naturalHeight","complete","Error","context","getContext","drawImage","children","createDocumentFragment","child","childNodes","clone","append","length","attribute","removeAttribute","Array","from","attributes","test","name","HTMLCanvasElement","childClone","SplitLayout","props","root","HTMLDivElement","pendingFrame","refs","Map","domIds","prefix","nextDomId","nextDragId","dragPreview","drag","setDrag","id","token","drop","setDrop","clearDrag","remove","createDragPreview","preview","inert","body","domId","existing","get","created","set","leaves","center","leafMap","map","frames","branches","values","flatMap","frame","node","kind","current","has","target","allowedTarget","transfer","DataTransfer","types","dropLabel","placement","branchMap","schedule","action","cancelAnimationFrame","requestAnimationFrame","active","ownerDocument","activeElement","contains","field","HTMLTextAreaElement","HTMLInputElement","selection","start","selectionStart","end","selectionEnd","direction","selectionDirection","isConnected","focus","preventScroll","setSelectionRange","clear","close","next","_el$","_tmpl$","_el$2","firstChild","_$use","el","_$insert","_$createComponent","each","initial","content","paneLabel","actions","delete","_el$3","_tmpl$2","_el$4","_el$5","_el$6","_el$7","nextSibling","$$focusin","addEventListener","event","dataTransfer","clientX","clientY","currentTarget","getBoundingClientRect","accepted","getData","preventDefault","relatedTarget","dropEffect","_$setAttribute","setData","effectAllowed","setDragImage","previewFrame","scaleX","scaleY","_c$","_$memo","_c$2","variant","aria-label","onClick","_c$3","_el$8","_tmpl$3","_$effect","_$className","_p$","_v$4","_v$5","_v$6","_v$7","focusedLeafId","_v$8","_v$9","_v$0","_v$1","_v$10","Boolean","_v$11","_v$12","e","t","a","o","i","n","s","h","_$style","r","d","l","orientation","sizes","keyboardDelta","onSizesChange","getClientRects","some","Number","isFinite","abs","style","Panel","minSize","maxSize","Handle","aria-orientation","aria-controls","join","aria-valuenow","_v$","_v$2","_v$3","_$delegateEvents","_$createComponent","_$memo","_$className","_$setAttribute","_$style","_$delegateEvents"],"sources":["../../../../src/components/layout/split-layout/split-layout.tsx"],"sourcesContent":["import Resizable from '@corvu/resizable'\nimport {\n For,\n createMemo,\n createComputed,\n createUniqueId,\n createSignal,\n onCleanup,\n on,\n type Accessor,\n type JSX,\n} from 'solid-js'\nimport { X, GripVertical } from 'lucide-solid'\nimport { Button } from '../../ui/button'\nimport { cn } from '#lib/utils'\nimport { computeLayoutFrames, type LayoutRect } from './geometry'\nimport { paneDropIntent, type PaneDropIntent } from './drop'\nexport type { PaneDropIntent } from './drop'\nconst PANE_DRAG_TYPE = 'application/x-adea-pane-move'\nconst DRAG_PREVIEW_MAX_WIDTH = 384\nconst DRAG_PREVIEW_MAX_HEIGHT = 256\nimport {\n listLeaves,\n MIN_SPLIT_RATIO,\n MAX_SPLIT_RATIO,\n type SplitLayoutBranch,\n type SplitLayoutLeaf,\n type SplitLayoutState,\n} from './model'\n\nexport type SplitLayoutProps<L extends SplitLayoutLeaf> = {\n state: SplitLayoutState<L>\n label: string\n /** Accessible name for a pane region and its close button. */\n labelForLeaf: (leaf: L) => string\n /** Called once per stable leaf owner; the accessor tracks opaque payload replacement. */\n renderLeaf: (leaf: Accessor<L>) => JSX.Element\n /** Inline visible header composition, separate from the region and close-button name. */\n renderPaneLabel?: (leaf: Accessor<L>) => JSX.Element\n /** Whether pane regions participate in sequential keyboard navigation; defaults to programmatic focus only. */\n paneTabIndex?: 0 | -1\n /** Accessible splitter name; defaults to the current orientation-specific label. */\n labelForSeparator?: (branch: SplitLayoutBranch<L>) => string\n onResize: (splitId: string, ratio: number) => void\n onFocus?: (leafId: string) => void\n /** Host performs the model transition and returns the surviving focus destination. */\n onClose?: (leafId: string) => string | undefined\n /** Pointer placement only; the host supplies keyboard commands and performs the model transition. */\n onMove?: (leafId: string, targetId: string, intent: PaneDropIntent) => void\n /** Stable header composition for host-owned keyboard actions/capability feedback. */\n renderPaneActions?: (leaf: Accessor<L>) => JSX.Element\n class?: string\n}\nfunction rectStyle(rect: LayoutRect): JSX.CSSProperties {\n return {\n left: `${rect.x * 100}%`,\n top: `${rect.y * 100}%`,\n width: `${rect.width * 100}%`,\n height: `${rect.height * 100}%`,\n }\n}\nfunction dragPreviewPlaceholder(source: HTMLElement, message: string) {\n const placeholder = document.createElement('div')\n placeholder.className = cn(\n source.getAttribute('class'),\n 'flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground'\n )\n placeholder.setAttribute('data-split-drag-placeholder', source.localName)\n placeholder.textContent = message\n return placeholder\n}\nfunction boundedSnapshotSize(width: number, height: number) {\n const sourceWidth = Math.max(1, width)\n const sourceHeight = Math.max(1, height)\n const scale = Math.min(\n 1,\n DRAG_PREVIEW_MAX_WIDTH / sourceWidth,\n DRAG_PREVIEW_MAX_HEIGHT / sourceHeight\n )\n return {\n width: Math.max(1, Math.round(sourceWidth * scale)),\n height: Math.max(1, Math.round(sourceHeight * scale)),\n }\n}\nfunction cloneDragPreviewNode(source: Node): Node | undefined {\n if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false)\n if (!(source instanceof HTMLElement || source instanceof SVGElement))\n return source.cloneNode(false)\n\n if (['script', 'style', 'link', 'source', 'track', 'meta'].includes(source.localName))\n return undefined\n if (['iframe', 'video', 'audio', 'object', 'embed'].includes(source.localName))\n return dragPreviewPlaceholder(\n source as HTMLElement,\n 'Embedded content unavailable while moving'\n )\n if (source instanceof HTMLImageElement) {\n const snapshot = document.createElement('canvas')\n const size = boundedSnapshotSize(\n source.naturalWidth || source.width,\n source.naturalHeight || source.height\n )\n snapshot.width = size.width\n snapshot.height = size.height\n snapshot.className = source.getAttribute('class') ?? ''\n snapshot.setAttribute('data-split-drag-image', '')\n try {\n if (!source.complete || source.naturalWidth === 0)\n throw new Error('image has no decoded pixels')\n const context = snapshot.getContext('2d')\n if (!context) throw new Error('canvas snapshot context is unavailable')\n context.drawImage(source, 0, 0, snapshot.width, snapshot.height)\n return snapshot\n } catch {\n return dragPreviewPlaceholder(source, 'Image preview unavailable')\n }\n }\n\n // Copying a defined custom element can run its constructor; inserting it can\n // also reconnect a stream or terminal session. Flatten its display tree.\n if (source instanceof HTMLElement && source.localName.includes('-')) {\n const children = document.createDocumentFragment()\n for (const child of source.childNodes) {\n const clone = cloneDragPreviewNode(child)\n if (clone) children.append(clone)\n }\n return children.childNodes.length > 0\n ? children\n : dragPreviewPlaceholder(source, 'Live component preview unavailable while moving')\n }\n\n const clone = source.cloneNode(false) as HTMLElement | SVGElement\n for (const attribute of [\n 'id',\n 'style',\n 'draggable',\n 'data-pane-drag-handle',\n 'data-pane-id',\n 'src',\n 'srcset',\n 'href',\n 'xlink:href',\n 'data',\n 'poster',\n ])\n clone.removeAttribute(attribute)\n for (const attribute of Array.from(clone.attributes))\n if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name)\n\n if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {\n const size = boundedSnapshotSize(source.width, source.height)\n clone.width = size.width\n clone.height = size.height\n try {\n const context = clone.getContext('2d')\n if (!context) throw new Error('canvas snapshot context is unavailable')\n context.drawImage(source, 0, 0, clone.width, clone.height)\n } catch {\n return dragPreviewPlaceholder(source, 'Canvas preview unavailable while moving')\n }\n return clone\n }\n\n for (const child of source.childNodes) {\n const childClone = cloneDragPreviewNode(child)\n if (childClone) clone.append(childClone)\n }\n return clone\n}\n/** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */\nexport function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {\n let root: HTMLDivElement | undefined\n let pendingFrame: number | undefined\n const refs = new Map<string, HTMLElement>()\n const domIds = new Map<string, string>()\n const prefix = createUniqueId()\n let nextDomId = 0\n let nextDragId = 0\n let dragPreview: HTMLElement | undefined\n const [drag, setDrag] = createSignal<{ id: string; token: string }>()\n const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()\n const clearDrag = () => {\n setDrag(undefined)\n setDrop(undefined)\n dragPreview?.remove()\n dragPreview = undefined\n }\n const createDragPreview = (source: HTMLElement) => {\n dragPreview?.remove()\n const preview = cloneDragPreviewNode(source) as HTMLElement\n preview.className =\n 'pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl'\n preview.removeAttribute('id')\n preview.removeAttribute('style')\n preview.removeAttribute('role')\n preview.removeAttribute('aria-label')\n preview.removeAttribute('data-pane-id')\n preview.removeAttribute('data-focused')\n preview.removeAttribute('data-drag-source')\n preview.removeAttribute('data-drop-direction')\n preview.removeAttribute('data-drop-placement')\n preview.setAttribute('data-split-drag-preview', '')\n preview.setAttribute('aria-hidden', 'true')\n preview.inert = true\n document.body.append(preview)\n dragPreview = preview\n return preview\n }\n const domId = (id: string) => {\n const existing = domIds.get(id)\n if (existing) return existing\n const created = `${prefix}-pane-${nextDomId++}`\n domIds.set(id, created)\n return created\n }\n const leaves = createMemo(() => listLeaves(props.state.center))\n const leafMap = createMemo(() => new Map(leaves().map((leaf) => [leaf.id, leaf])))\n const frames = createMemo(() => computeLayoutFrames(props.state.center))\n const branches = createMemo(() =>\n Array.from(frames().values()).flatMap((frame) =>\n frame.node.kind === 'split' ? [frame.node] : []\n )\n )\n createComputed(() => {\n const current = drag()\n if (current && (!props.onMove || !leafMap().has(current.id))) clearDrag()\n const target = drop()\n if (target && !leafMap().has(target.id)) setDrop(undefined)\n })\n const allowedTarget = (id: string, transfer: DataTransfer | null) => {\n const current = drag()\n return props.onMove &&\n current &&\n current.id !== id &&\n leafMap().has(current.id) &&\n leafMap().has(id) &&\n transfer?.types.includes(PANE_DRAG_TYPE)\n ? current\n : undefined\n }\n const dropLabel = createMemo(() => {\n const target = drop()\n const leaf = target ? leafMap().get(target.id) : undefined\n return target && leaf\n ? `Drop to place pane ${target.intent.placement} ${props.labelForLeaf(leaf)}`\n : ''\n })\n const branchMap = createMemo(() => new Map(branches().map((branch) => [branch.id, branch])))\n const schedule = (action: () => void) => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n pendingFrame = requestAnimationFrame(() => {\n pendingFrame = undefined\n action()\n })\n }\n // Register before keyed children update: capture a focused editor before an\n // ordered DOM move can blur it. Never override a newer focus outside this root.\n createComputed(\n on(\n () => props.state.center,\n () => {\n const active = root?.ownerDocument.activeElement\n if (!root || !(active instanceof HTMLElement) || !root.contains(active)) return\n const field =\n active instanceof HTMLTextAreaElement || active instanceof HTMLInputElement\n ? active\n : undefined\n const selection = field\n ? {\n start: field.selectionStart,\n end: field.selectionEnd,\n direction: field.selectionDirection,\n }\n : undefined\n schedule(() => {\n if (!active.isConnected || !root?.contains(active)) return\n const current = active.ownerDocument.activeElement\n if (current !== active && current !== active.ownerDocument.body) return\n active.focus({ preventScroll: true })\n if (field && selection?.start !== null && selection?.end !== null && selection)\n field.setSelectionRange(\n selection.start,\n selection.end,\n selection.direction ?? undefined\n )\n })\n }\n )\n )\n onCleanup(() => {\n if (pendingFrame !== undefined) cancelAnimationFrame(pendingFrame)\n clearDrag()\n refs.clear()\n domIds.clear()\n })\n const close = (id: string) => {\n const next = props.onClose?.(id)\n if (next) schedule(() => refs.get(next)?.focus({ preventScroll: true }))\n }\n return (\n <div\n ref={(el) => {\n root = el\n }}\n role=\"group\"\n aria-label={props.label}\n data-slot=\"split-layout\"\n data-dragging={drag() ? '' : undefined}\n class={cn('relative size-full min-h-0 min-w-0 data-[dragging]:cursor-grabbing', props.class)}\n >\n <For each={leaves().map((leaf) => leaf.id)}>\n {(id) => {\n const initial = leafMap().get(id)!\n const leaf = () => leafMap().get(id) ?? initial\n const content = props.renderLeaf(leaf)\n const paneLabel = props.renderPaneLabel?.(leaf)\n const actions = props.renderPaneActions?.(leaf)\n const intent = () => (drop()?.id === id ? drop()?.intent : undefined)\n onCleanup(() => {\n refs.delete(id)\n domIds.delete(id)\n })\n return (\n <section\n ref={(el) => refs.set(id, el)}\n id={domId(id)}\n role=\"region\"\n aria-label={props.labelForLeaf(leaf())}\n tabIndex={props.paneTabIndex ?? -1}\n data-pane-id={id}\n data-focused={props.state.focusedLeafId === id ? '' : undefined}\n data-drag-source={drag()?.id === id ? '' : undefined}\n data-drop-direction={intent()?.direction}\n data-drop-placement={intent()?.placement}\n onDragOver={(event) => {\n if (!allowedTarget(id, event.dataTransfer)) return\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n if (!next) {\n setDrop(undefined)\n return\n }\n event.preventDefault()\n if (event.dataTransfer) event.dataTransfer.dropEffect = 'move'\n setDrop({ id, intent: next })\n }}\n onDragLeave={(event) => {\n if (\n event.relatedTarget instanceof Node &&\n event.currentTarget.contains(event.relatedTarget)\n )\n return\n if (drop()?.id === id) setDrop(undefined)\n }}\n onDrop={(event) => {\n const current = allowedTarget(id, event.dataTransfer)\n const next = paneDropIntent(\n event.clientX,\n event.clientY,\n event.currentTarget.getBoundingClientRect()\n )\n const accepted =\n current && next && event.dataTransfer?.getData(PANE_DRAG_TYPE) === current.token\n clearDrag()\n if (!accepted) return\n event.preventDefault()\n props.onMove?.(current.id, id, next)\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring\"\n onFocusIn={() => props.onFocus?.(id)}\n >\n <header class=\"flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2\">\n <span\n data-pane-drag-handle=\"\"\n draggable={Boolean(props.onMove)}\n class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {\n 'cursor-grab': Boolean(props.onMove),\n 'cursor-grabbing': drag()?.id === id,\n })}\n title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}\n onDragStart={(event) => {\n if (!props.onMove || !event.dataTransfer) {\n event.preventDefault()\n return\n }\n const token = `${prefix}-${++nextDragId}`\n setDrag({ id, token })\n event.dataTransfer.setData(PANE_DRAG_TYPE, token)\n event.dataTransfer.effectAllowed = 'move'\n // The native drag image carries this pane's actual content\n // in a bounded floating-window frame; the source recedes\n // independently as the browser follows the image pointer.\n const source = refs.get(id)\n const frame = source?.getBoundingClientRect()\n if (source && frame && typeof event.dataTransfer.setDragImage === 'function') {\n const preview = createDragPreview(source)\n const previewFrame = preview.getBoundingClientRect()\n const scaleX = previewFrame.width / frame.width\n const scaleY = previewFrame.height / frame.height\n event.dataTransfer.setDragImage(\n preview,\n Math.max(0, Math.round((event.clientX - frame.left) * scaleX)),\n Math.max(0, Math.round((event.clientY - frame.top) * scaleY))\n )\n preview.setAttribute('data-native-drag-image', '')\n }\n }}\n onDragEnd={clearDrag}\n >\n {props.onMove ? (\n <GripVertical class=\"size-3 shrink-0\" aria-hidden=\"true\" />\n ) : null}\n <span class=\"min-w-0 flex-1 truncate\">\n {paneLabel ?? props.labelForLeaf(leaf())}\n </span>\n </span>\n {actions}\n {props.onClose ? (\n <Button\n variant=\"ghost\"\n size=\"icon-xs\"\n aria-label={`Close ${props.labelForLeaf(leaf())}`}\n onClick={() => close(id)}\n >\n <X />\n </Button>\n ) : null}\n </header>\n <div class=\"min-h-0 min-w-0 flex-1 overflow-auto\">{content}</div>\n {intent() ? (\n <div\n aria-hidden=\"true\"\n class={cn('pointer-events-none absolute border-2 border-primary bg-primary/10', {\n 'inset-y-0 left-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'before',\n 'inset-y-0 right-0 w-1/2':\n intent()?.direction === 'row' && intent()?.placement === 'after',\n 'inset-x-0 top-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'before',\n 'inset-x-0 bottom-0 h-1/2':\n intent()?.direction === 'column' && intent()?.placement === 'after',\n })}\n />\n ) : null}\n </section>\n )\n }}\n </For>\n <span class=\"sr-only\" role=\"status\">\n {dropLabel()}\n </span>\n <For each={branches().map((branch) => branch.id)}>\n {(id) => {\n const initial = branchMap().get(id)!\n const branch: Accessor<SplitLayoutBranch<L>> = () => branchMap().get(id) ?? initial\n return (\n <Resizable\n orientation={branch().direction === 'row' ? 'horizontal' : 'vertical'}\n sizes={[branch().ratio, 1 - branch().ratio]}\n keyboardDelta={0.05}\n onSizesChange={(sizes) => {\n // Corvu also reports panel registration/unregistration while\n // mounting or disposing a controller. Those incomplete or\n // hidden-host sizes are not user resize intent.\n if (!root?.isConnected || root.getClientRects().length === 0) return\n if (\n sizes.length !== 2 ||\n sizes.some((size) => !Number.isFinite(size)) ||\n Math.abs(sizes[0]! + sizes[1]! - 1) > 0.000001\n )\n return\n if (sizes[0] !== undefined && Math.abs(sizes[0] - branch().ratio) > 0.000001)\n props.onResize(id, sizes[0])\n }}\n style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}\n class=\"pointer-events-none absolute flex min-h-0 min-w-0 data-[orientation=vertical]:flex-col\"\n >\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n <Resizable.Handle\n aria-label={\n props.labelForSeparator?.(branch()) ??\n (branch().direction === 'row' ? 'Resize pane columns' : 'Resize pane rows')\n }\n aria-orientation={branch().direction === 'row' ? 'vertical' : 'horizontal'}\n aria-controls={listLeaves(branch())\n .map((leaf) => domId(leaf.id))\n .join(' ')}\n aria-valuemin={10}\n aria-valuemax={90}\n aria-valuenow={Math.round(branch().ratio * 100)}\n class=\"pointer-events-auto relative w-px shrink-0 bg-border after:absolute after:inset-y-0 after:-inset-x-1 after:w-3 focus-visible:bg-primary focus-visible:outline-none hover:bg-primary data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:inset-x-0 data-[orientation=vertical]:after:-inset-y-1 data-[orientation=vertical]:after:h-3 data-[orientation=vertical]:after:w-full\"\n />\n <Resizable.Panel\n minSize={MIN_SPLIT_RATIO}\n maxSize={MAX_SPLIT_RATIO}\n aria-hidden=\"true\"\n />\n </Resizable>\n )\n }}\n </For>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -4,6 +4,7 @@
4
4
  "title": "Catalog Browser",
5
5
  "description": "",
6
6
  "dependencies": [
7
+ "class-variance-authority@^0.7.1",
7
8
  "lucide-solid@^1.48.0"
8
9
  ],
9
10
  "registryDependencies": [
@@ -1956,6 +1956,7 @@
1956
1956
  "title": "Catalog Browser",
1957
1957
  "description": "",
1958
1958
  "dependencies": [
1959
+ "class-variance-authority@^0.7.1",
1959
1960
  "lucide-solid@^1.48.0"
1960
1961
  ],
1961
1962
  "registryDependencies": [
@@ -1,5 +1,27 @@
1
- import { createUniqueId, Show, type JSX } from 'solid-js'
1
+ import { createContext, createUniqueId, Show, useContext, type JSX } from 'solid-js'
2
2
  import { Badge } from '../../ui/badge'
3
+ import { cva } from 'class-variance-authority'
4
+
5
+ type SectionsLayout = 'stacked' | 'columns'
6
+ const SectionsLayoutContext = createContext<() => SectionsLayout>((): SectionsLayout => 'stacked')
7
+ const sectionsVariants = cva('grid min-w-0', {
8
+ variants: {
9
+ layout: {
10
+ stacked: 'gap-2',
11
+ columns:
12
+ 'gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0',
13
+ },
14
+ },
15
+ })
16
+ const sectionVariants = cva('grid min-w-0 gap-1.5 wrap-anywhere', {
17
+ variants: {
18
+ layout: {
19
+ stacked: 'rounded-xl border border-border p-3',
20
+ columns:
21
+ 'md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0',
22
+ },
23
+ },
24
+ })
3
25
 
4
26
  /** Shared item summary and action frame for catalog-owned detail content. */
5
27
  export function CatalogDetail(props: {
@@ -13,6 +35,8 @@ export function CatalogDetail(props: {
13
35
  badges?: JSX.Element
14
36
  action?: JSX.Element
15
37
  status?: JSX.Element
38
+ /** Groups related fields in one padded card; stacks them on narrow screens. */
39
+ sectionsLayout?: SectionsLayout
16
40
  children?: JSX.Element
17
41
  }) {
18
42
  return (
@@ -40,19 +64,25 @@ export function CatalogDetail(props: {
40
64
  </div>
41
65
  </Show>
42
66
  </div>
43
- <div class="grid min-w-0 gap-2">{props.children}</div>
67
+ <SectionsLayoutContext.Provider value={() => props.sectionsLayout ?? 'stacked'}>
68
+ <div
69
+ data-catalog-detail-sections
70
+ data-layout={props.sectionsLayout ?? 'stacked'}
71
+ class={sectionsVariants({ layout: props.sectionsLayout ?? 'stacked' })}
72
+ >
73
+ {props.children}
74
+ </div>
75
+ </SectionsLayoutContext.Provider>
44
76
  </article>
45
77
  )
46
78
  }
47
79
 
48
80
  /** A shared compact row for host-specific fields, permissions, and copy. */
49
81
  export function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {
82
+ const layout = useContext(SectionsLayoutContext)
50
83
  const headingId = `catalog-detail-section-${createUniqueId()}`
51
84
  return (
52
- <section
53
- class="grid min-w-0 gap-1.5 rounded-xl border border-border p-3"
54
- aria-labelledby={headingId}
55
- >
85
+ <section class={sectionVariants({ layout: layout() })} aria-labelledby={headingId}>
56
86
  <h3
57
87
  id={headingId}
58
88
  class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"
@@ -17,6 +17,8 @@ import { computeLayoutFrames, type LayoutRect } from './geometry'
17
17
  import { paneDropIntent, type PaneDropIntent } from './drop'
18
18
  export type { PaneDropIntent } from './drop'
19
19
  const PANE_DRAG_TYPE = 'application/x-adea-pane-move'
20
+ const DRAG_PREVIEW_MAX_WIDTH = 384
21
+ const DRAG_PREVIEW_MAX_HEIGHT = 256
20
22
  import {
21
23
  listLeaves,
22
24
  MIN_SPLIT_RATIO,
@@ -57,6 +59,114 @@ function rectStyle(rect: LayoutRect): JSX.CSSProperties {
57
59
  height: `${rect.height * 100}%`,
58
60
  }
59
61
  }
62
+ function dragPreviewPlaceholder(source: HTMLElement, message: string) {
63
+ const placeholder = document.createElement('div')
64
+ placeholder.className = cn(
65
+ source.getAttribute('class'),
66
+ 'flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground'
67
+ )
68
+ placeholder.setAttribute('data-split-drag-placeholder', source.localName)
69
+ placeholder.textContent = message
70
+ return placeholder
71
+ }
72
+ function boundedSnapshotSize(width: number, height: number) {
73
+ const sourceWidth = Math.max(1, width)
74
+ const sourceHeight = Math.max(1, height)
75
+ const scale = Math.min(
76
+ 1,
77
+ DRAG_PREVIEW_MAX_WIDTH / sourceWidth,
78
+ DRAG_PREVIEW_MAX_HEIGHT / sourceHeight
79
+ )
80
+ return {
81
+ width: Math.max(1, Math.round(sourceWidth * scale)),
82
+ height: Math.max(1, Math.round(sourceHeight * scale)),
83
+ }
84
+ }
85
+ function cloneDragPreviewNode(source: Node): Node | undefined {
86
+ if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false)
87
+ if (!(source instanceof HTMLElement || source instanceof SVGElement))
88
+ return source.cloneNode(false)
89
+
90
+ if (['script', 'style', 'link', 'source', 'track', 'meta'].includes(source.localName))
91
+ return undefined
92
+ if (['iframe', 'video', 'audio', 'object', 'embed'].includes(source.localName))
93
+ return dragPreviewPlaceholder(
94
+ source as HTMLElement,
95
+ 'Embedded content unavailable while moving'
96
+ )
97
+ if (source instanceof HTMLImageElement) {
98
+ const snapshot = document.createElement('canvas')
99
+ const size = boundedSnapshotSize(
100
+ source.naturalWidth || source.width,
101
+ source.naturalHeight || source.height
102
+ )
103
+ snapshot.width = size.width
104
+ snapshot.height = size.height
105
+ snapshot.className = source.getAttribute('class') ?? ''
106
+ snapshot.setAttribute('data-split-drag-image', '')
107
+ try {
108
+ if (!source.complete || source.naturalWidth === 0)
109
+ throw new Error('image has no decoded pixels')
110
+ const context = snapshot.getContext('2d')
111
+ if (!context) throw new Error('canvas snapshot context is unavailable')
112
+ context.drawImage(source, 0, 0, snapshot.width, snapshot.height)
113
+ return snapshot
114
+ } catch {
115
+ return dragPreviewPlaceholder(source, 'Image preview unavailable')
116
+ }
117
+ }
118
+
119
+ // Copying a defined custom element can run its constructor; inserting it can
120
+ // also reconnect a stream or terminal session. Flatten its display tree.
121
+ if (source instanceof HTMLElement && source.localName.includes('-')) {
122
+ const children = document.createDocumentFragment()
123
+ for (const child of source.childNodes) {
124
+ const clone = cloneDragPreviewNode(child)
125
+ if (clone) children.append(clone)
126
+ }
127
+ return children.childNodes.length > 0
128
+ ? children
129
+ : dragPreviewPlaceholder(source, 'Live component preview unavailable while moving')
130
+ }
131
+
132
+ const clone = source.cloneNode(false) as HTMLElement | SVGElement
133
+ for (const attribute of [
134
+ 'id',
135
+ 'style',
136
+ 'draggable',
137
+ 'data-pane-drag-handle',
138
+ 'data-pane-id',
139
+ 'src',
140
+ 'srcset',
141
+ 'href',
142
+ 'xlink:href',
143
+ 'data',
144
+ 'poster',
145
+ ])
146
+ clone.removeAttribute(attribute)
147
+ for (const attribute of Array.from(clone.attributes))
148
+ if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name)
149
+
150
+ if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {
151
+ const size = boundedSnapshotSize(source.width, source.height)
152
+ clone.width = size.width
153
+ clone.height = size.height
154
+ try {
155
+ const context = clone.getContext('2d')
156
+ if (!context) throw new Error('canvas snapshot context is unavailable')
157
+ context.drawImage(source, 0, 0, clone.width, clone.height)
158
+ } catch {
159
+ return dragPreviewPlaceholder(source, 'Canvas preview unavailable while moving')
160
+ }
161
+ return clone
162
+ }
163
+
164
+ for (const child of source.childNodes) {
165
+ const childClone = cloneDragPreviewNode(child)
166
+ if (childClone) clone.append(childClone)
167
+ }
168
+ return clone
169
+ }
60
170
  /** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */
61
171
  export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {
62
172
  let root: HTMLDivElement | undefined
@@ -66,11 +176,35 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
66
176
  const prefix = createUniqueId()
67
177
  let nextDomId = 0
68
178
  let nextDragId = 0
179
+ let dragPreview: HTMLElement | undefined
69
180
  const [drag, setDrag] = createSignal<{ id: string; token: string }>()
70
181
  const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()
71
182
  const clearDrag = () => {
72
183
  setDrag(undefined)
73
184
  setDrop(undefined)
185
+ dragPreview?.remove()
186
+ dragPreview = undefined
187
+ }
188
+ const createDragPreview = (source: HTMLElement) => {
189
+ dragPreview?.remove()
190
+ const preview = cloneDragPreviewNode(source) as HTMLElement
191
+ preview.className =
192
+ 'pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl'
193
+ preview.removeAttribute('id')
194
+ preview.removeAttribute('style')
195
+ preview.removeAttribute('role')
196
+ preview.removeAttribute('aria-label')
197
+ preview.removeAttribute('data-pane-id')
198
+ preview.removeAttribute('data-focused')
199
+ preview.removeAttribute('data-drag-source')
200
+ preview.removeAttribute('data-drop-direction')
201
+ preview.removeAttribute('data-drop-placement')
202
+ preview.setAttribute('data-split-drag-preview', '')
203
+ preview.setAttribute('aria-hidden', 'true')
204
+ preview.inert = true
205
+ document.body.append(preview)
206
+ dragPreview = preview
207
+ return preview
74
208
  }
75
209
  const domId = (id: string) => {
76
210
  const existing = domIds.get(id)
@@ -236,7 +370,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
236
370
  props.onMove?.(current.id, id, next)
237
371
  }}
238
372
  style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}
239
- class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
373
+ class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
240
374
  onFocusIn={() => props.onFocus?.(id)}
241
375
  >
242
376
  <header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2">
@@ -245,6 +379,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
245
379
  draggable={Boolean(props.onMove)}
246
380
  class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {
247
381
  'cursor-grab': Boolean(props.onMove),
382
+ 'cursor-grabbing': drag()?.id === id,
248
383
  })}
249
384
  title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}
250
385
  onDragStart={(event) => {
@@ -256,18 +391,23 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
256
391
  setDrag({ id, token })
257
392
  event.dataTransfer.setData(PANE_DRAG_TYPE, token)
258
393
  event.dataTransfer.effectAllowed = 'move'
259
- // The ghost is the pane itself, not the handle strip: the
260
- // browser snapshots the region before the drag state dims
261
- // this section, so the dragged pane stays readable while
262
- // its source visibly recedes.
394
+ // The native drag image carries this pane's actual content
395
+ // in a bounded floating-window frame; the source recedes
396
+ // independently as the browser follows the image pointer.
263
397
  const source = refs.get(id)
264
398
  const frame = source?.getBoundingClientRect()
265
- if (source && frame && typeof event.dataTransfer.setDragImage === 'function')
399
+ if (source && frame && typeof event.dataTransfer.setDragImage === 'function') {
400
+ const preview = createDragPreview(source)
401
+ const previewFrame = preview.getBoundingClientRect()
402
+ const scaleX = previewFrame.width / frame.width
403
+ const scaleY = previewFrame.height / frame.height
266
404
  event.dataTransfer.setDragImage(
267
- source,
268
- Math.max(0, Math.round(event.clientX - frame.left)),
269
- Math.max(0, Math.round(event.clientY - frame.top))
405
+ preview,
406
+ Math.max(0, Math.round((event.clientX - frame.left) * scaleX)),
407
+ Math.max(0, Math.round((event.clientY - frame.top) * scaleY))
270
408
  )
409
+ preview.setAttribute('data-native-drag-image', '')
410
+ }
271
411
  }}
272
412
  onDragEnd={clearDrag}
273
413
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.103.1",
3
+ "version": "0.104.0",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
package/registry.json CHANGED
@@ -1956,6 +1956,7 @@
1956
1956
  "title": "Catalog Browser",
1957
1957
  "description": "",
1958
1958
  "dependencies": [
1959
+ "class-variance-authority@^0.7.1",
1959
1960
  "lucide-solid@^1.48.0"
1960
1961
  ],
1961
1962
  "registryDependencies": [
@@ -1,5 +1,27 @@
1
- import { createUniqueId, Show, type JSX } from 'solid-js'
1
+ import { createContext, createUniqueId, Show, useContext, type JSX } from 'solid-js'
2
2
  import { Badge } from '../../ui/badge'
3
+ import { cva } from 'class-variance-authority'
4
+
5
+ type SectionsLayout = 'stacked' | 'columns'
6
+ const SectionsLayoutContext = createContext<() => SectionsLayout>((): SectionsLayout => 'stacked')
7
+ const sectionsVariants = cva('grid min-w-0', {
8
+ variants: {
9
+ layout: {
10
+ stacked: 'gap-2',
11
+ columns:
12
+ 'gap-4 rounded-xl border border-border p-3 md:auto-cols-fr md:grid-flow-col md:gap-0',
13
+ },
14
+ },
15
+ })
16
+ const sectionVariants = cva('grid min-w-0 gap-1.5 wrap-anywhere', {
17
+ variants: {
18
+ layout: {
19
+ stacked: 'rounded-xl border border-border p-3',
20
+ columns:
21
+ 'md:border-s md:border-border md:px-3 md:first:border-s-0 md:first:ps-0 md:last:pe-0',
22
+ },
23
+ },
24
+ })
3
25
 
4
26
  /** Shared item summary and action frame for catalog-owned detail content. */
5
27
  export function CatalogDetail(props: {
@@ -13,6 +35,8 @@ export function CatalogDetail(props: {
13
35
  badges?: JSX.Element
14
36
  action?: JSX.Element
15
37
  status?: JSX.Element
38
+ /** Groups related fields in one padded card; stacks them on narrow screens. */
39
+ sectionsLayout?: SectionsLayout
16
40
  children?: JSX.Element
17
41
  }) {
18
42
  return (
@@ -40,19 +64,25 @@ export function CatalogDetail(props: {
40
64
  </div>
41
65
  </Show>
42
66
  </div>
43
- <div class="grid min-w-0 gap-2">{props.children}</div>
67
+ <SectionsLayoutContext.Provider value={() => props.sectionsLayout ?? 'stacked'}>
68
+ <div
69
+ data-catalog-detail-sections
70
+ data-layout={props.sectionsLayout ?? 'stacked'}
71
+ class={sectionsVariants({ layout: props.sectionsLayout ?? 'stacked' })}
72
+ >
73
+ {props.children}
74
+ </div>
75
+ </SectionsLayoutContext.Provider>
44
76
  </article>
45
77
  )
46
78
  }
47
79
 
48
80
  /** A shared compact row for host-specific fields, permissions, and copy. */
49
81
  export function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {
82
+ const layout = useContext(SectionsLayoutContext)
50
83
  const headingId = `catalog-detail-section-${createUniqueId()}`
51
84
  return (
52
- <section
53
- class="grid min-w-0 gap-1.5 rounded-xl border border-border p-3"
54
- aria-labelledby={headingId}
55
- >
85
+ <section class={sectionVariants({ layout: layout() })} aria-labelledby={headingId}>
56
86
  <h3
57
87
  id={headingId}
58
88
  class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"
@@ -17,6 +17,8 @@ import { computeLayoutFrames, type LayoutRect } from './geometry'
17
17
  import { paneDropIntent, type PaneDropIntent } from './drop'
18
18
  export type { PaneDropIntent } from './drop'
19
19
  const PANE_DRAG_TYPE = 'application/x-adea-pane-move'
20
+ const DRAG_PREVIEW_MAX_WIDTH = 384
21
+ const DRAG_PREVIEW_MAX_HEIGHT = 256
20
22
  import {
21
23
  listLeaves,
22
24
  MIN_SPLIT_RATIO,
@@ -57,6 +59,114 @@ function rectStyle(rect: LayoutRect): JSX.CSSProperties {
57
59
  height: `${rect.height * 100}%`,
58
60
  }
59
61
  }
62
+ function dragPreviewPlaceholder(source: HTMLElement, message: string) {
63
+ const placeholder = document.createElement('div')
64
+ placeholder.className = cn(
65
+ source.getAttribute('class'),
66
+ 'flex min-h-8 min-w-0 items-center justify-center bg-muted px-2 text-center text-xs text-muted-foreground'
67
+ )
68
+ placeholder.setAttribute('data-split-drag-placeholder', source.localName)
69
+ placeholder.textContent = message
70
+ return placeholder
71
+ }
72
+ function boundedSnapshotSize(width: number, height: number) {
73
+ const sourceWidth = Math.max(1, width)
74
+ const sourceHeight = Math.max(1, height)
75
+ const scale = Math.min(
76
+ 1,
77
+ DRAG_PREVIEW_MAX_WIDTH / sourceWidth,
78
+ DRAG_PREVIEW_MAX_HEIGHT / sourceHeight
79
+ )
80
+ return {
81
+ width: Math.max(1, Math.round(sourceWidth * scale)),
82
+ height: Math.max(1, Math.round(sourceHeight * scale)),
83
+ }
84
+ }
85
+ function cloneDragPreviewNode(source: Node): Node | undefined {
86
+ if (source.nodeType === Node.TEXT_NODE) return source.cloneNode(false)
87
+ if (!(source instanceof HTMLElement || source instanceof SVGElement))
88
+ return source.cloneNode(false)
89
+
90
+ if (['script', 'style', 'link', 'source', 'track', 'meta'].includes(source.localName))
91
+ return undefined
92
+ if (['iframe', 'video', 'audio', 'object', 'embed'].includes(source.localName))
93
+ return dragPreviewPlaceholder(
94
+ source as HTMLElement,
95
+ 'Embedded content unavailable while moving'
96
+ )
97
+ if (source instanceof HTMLImageElement) {
98
+ const snapshot = document.createElement('canvas')
99
+ const size = boundedSnapshotSize(
100
+ source.naturalWidth || source.width,
101
+ source.naturalHeight || source.height
102
+ )
103
+ snapshot.width = size.width
104
+ snapshot.height = size.height
105
+ snapshot.className = source.getAttribute('class') ?? ''
106
+ snapshot.setAttribute('data-split-drag-image', '')
107
+ try {
108
+ if (!source.complete || source.naturalWidth === 0)
109
+ throw new Error('image has no decoded pixels')
110
+ const context = snapshot.getContext('2d')
111
+ if (!context) throw new Error('canvas snapshot context is unavailable')
112
+ context.drawImage(source, 0, 0, snapshot.width, snapshot.height)
113
+ return snapshot
114
+ } catch {
115
+ return dragPreviewPlaceholder(source, 'Image preview unavailable')
116
+ }
117
+ }
118
+
119
+ // Copying a defined custom element can run its constructor; inserting it can
120
+ // also reconnect a stream or terminal session. Flatten its display tree.
121
+ if (source instanceof HTMLElement && source.localName.includes('-')) {
122
+ const children = document.createDocumentFragment()
123
+ for (const child of source.childNodes) {
124
+ const clone = cloneDragPreviewNode(child)
125
+ if (clone) children.append(clone)
126
+ }
127
+ return children.childNodes.length > 0
128
+ ? children
129
+ : dragPreviewPlaceholder(source, 'Live component preview unavailable while moving')
130
+ }
131
+
132
+ const clone = source.cloneNode(false) as HTMLElement | SVGElement
133
+ for (const attribute of [
134
+ 'id',
135
+ 'style',
136
+ 'draggable',
137
+ 'data-pane-drag-handle',
138
+ 'data-pane-id',
139
+ 'src',
140
+ 'srcset',
141
+ 'href',
142
+ 'xlink:href',
143
+ 'data',
144
+ 'poster',
145
+ ])
146
+ clone.removeAttribute(attribute)
147
+ for (const attribute of Array.from(clone.attributes))
148
+ if (/^on/i.test(attribute.name)) clone.removeAttribute(attribute.name)
149
+
150
+ if (source instanceof HTMLCanvasElement && clone instanceof HTMLCanvasElement) {
151
+ const size = boundedSnapshotSize(source.width, source.height)
152
+ clone.width = size.width
153
+ clone.height = size.height
154
+ try {
155
+ const context = clone.getContext('2d')
156
+ if (!context) throw new Error('canvas snapshot context is unavailable')
157
+ context.drawImage(source, 0, 0, clone.width, clone.height)
158
+ } catch {
159
+ return dragPreviewPlaceholder(source, 'Canvas preview unavailable while moving')
160
+ }
161
+ return clone
162
+ }
163
+
164
+ for (const child of source.childNodes) {
165
+ const childClone = cloneDragPreviewNode(child)
166
+ if (childClone) clone.append(childClone)
167
+ }
168
+ return clone
169
+ }
60
170
  /** Muxy frame geometry keeps leaf owners stable; Corvu owns constrained separator interactions. */
61
171
  export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L>) {
62
172
  let root: HTMLDivElement | undefined
@@ -66,11 +176,35 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
66
176
  const prefix = createUniqueId()
67
177
  let nextDomId = 0
68
178
  let nextDragId = 0
179
+ let dragPreview: HTMLElement | undefined
69
180
  const [drag, setDrag] = createSignal<{ id: string; token: string }>()
70
181
  const [drop, setDrop] = createSignal<{ id: string; intent: PaneDropIntent }>()
71
182
  const clearDrag = () => {
72
183
  setDrag(undefined)
73
184
  setDrop(undefined)
185
+ dragPreview?.remove()
186
+ dragPreview = undefined
187
+ }
188
+ const createDragPreview = (source: HTMLElement) => {
189
+ dragPreview?.remove()
190
+ const preview = cloneDragPreviewNode(source) as HTMLElement
191
+ preview.className =
192
+ 'pointer-events-none fixed -left-full top-0 z-(--z-dialog) flex h-64 max-h-screen w-96 max-w-screen min-h-0 min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground shadow-xl'
193
+ preview.removeAttribute('id')
194
+ preview.removeAttribute('style')
195
+ preview.removeAttribute('role')
196
+ preview.removeAttribute('aria-label')
197
+ preview.removeAttribute('data-pane-id')
198
+ preview.removeAttribute('data-focused')
199
+ preview.removeAttribute('data-drag-source')
200
+ preview.removeAttribute('data-drop-direction')
201
+ preview.removeAttribute('data-drop-placement')
202
+ preview.setAttribute('data-split-drag-preview', '')
203
+ preview.setAttribute('aria-hidden', 'true')
204
+ preview.inert = true
205
+ document.body.append(preview)
206
+ dragPreview = preview
207
+ return preview
74
208
  }
75
209
  const domId = (id: string) => {
76
210
  const existing = domIds.get(id)
@@ -236,7 +370,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
236
370
  props.onMove?.(current.id, id, next)
237
371
  }}
238
372
  style={rectStyle(frames().get(id)?.rect ?? { x: 0, y: 0, width: 0, height: 0 })}
239
- class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
373
+ class="absolute flex min-h-0 min-w-0 flex-col overflow-hidden border border-border transition-opacity motion-reduce:transition-none data-[drag-source]:opacity-40 data-[focused]:border-primary bg-background text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring"
240
374
  onFocusIn={() => props.onFocus?.(id)}
241
375
  >
242
376
  <header class="flex min-w-0 shrink-0 items-center gap-2 bg-surface px-2">
@@ -245,6 +379,7 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
245
379
  draggable={Boolean(props.onMove)}
246
380
  class={cn('flex min-w-0 flex-1 items-center gap-2 truncate text-sm', {
247
381
  'cursor-grab': Boolean(props.onMove),
382
+ 'cursor-grabbing': drag()?.id === id,
248
383
  })}
249
384
  title={props.onMove ? `Drag ${props.labelForLeaf(leaf())} to move` : undefined}
250
385
  onDragStart={(event) => {
@@ -256,18 +391,23 @@ export function SplitLayout<L extends SplitLayoutLeaf>(props: SplitLayoutProps<L
256
391
  setDrag({ id, token })
257
392
  event.dataTransfer.setData(PANE_DRAG_TYPE, token)
258
393
  event.dataTransfer.effectAllowed = 'move'
259
- // The ghost is the pane itself, not the handle strip: the
260
- // browser snapshots the region before the drag state dims
261
- // this section, so the dragged pane stays readable while
262
- // its source visibly recedes.
394
+ // The native drag image carries this pane's actual content
395
+ // in a bounded floating-window frame; the source recedes
396
+ // independently as the browser follows the image pointer.
263
397
  const source = refs.get(id)
264
398
  const frame = source?.getBoundingClientRect()
265
- if (source && frame && typeof event.dataTransfer.setDragImage === 'function')
399
+ if (source && frame && typeof event.dataTransfer.setDragImage === 'function') {
400
+ const preview = createDragPreview(source)
401
+ const previewFrame = preview.getBoundingClientRect()
402
+ const scaleX = previewFrame.width / frame.width
403
+ const scaleY = previewFrame.height / frame.height
266
404
  event.dataTransfer.setDragImage(
267
- source,
268
- Math.max(0, Math.round(event.clientX - frame.left)),
269
- Math.max(0, Math.round(event.clientY - frame.top))
405
+ preview,
406
+ Math.max(0, Math.round((event.clientX - frame.left) * scaleX)),
407
+ Math.max(0, Math.round((event.clientY - frame.top) * scaleY))
270
408
  )
409
+ preview.setAttribute('data-native-drag-image', '')
410
+ }
271
411
  }}
272
412
  onDragEnd={clearDrag}
273
413
  >