@dynamix-layout/core 1.1.1 → 2.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @dynamix-layout/core
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [#87](https://github.com/xcode-studio/dynamix-layout/pull/87) [`a31dc4c`](https://github.com/xcode-studio/dynamix-layout/commit/a31dc4c2aef4facb8b80f94b9e3d97c36e148ccc) Thanks [@akash-aman](https://github.com/akash-aman)! - **v2: an instance-based, pure layout engine.** See the migration guide for every rename.
8
+
9
+ - **`createLayout(options)` replaces `new DynamixLayoutCore()`.** Each instance is independent: there is no module-level state, no static caches, no timers, and nothing touches the DOM. Two layouts on a page, or one per server request, no longer interfere.
10
+ - **Immutable snapshots** (`getSnapshot()` / `subscribe()`) share unchanged parts by reference: `tabsets`, `tabs`, `splitters`, `rects` and `drag`.
11
+ - **Typed actions** that return whether anything changed and never throw: `moveTab`, `moveTabset`, `selectTab`, `addTab`, `removeTab`, `setTabs`, `resizeSplitter`, `moveSplitterBy`, `maximize`/`restore`, `fold`/`unfold`, plus one drag model for tabs and splitters (`startDrag`, `updateDrag`, `endDrag`, `cancelDrag`) and hit testing (`getDropTarget`, `listDropTargets`).
12
+ - **`onLayoutChange(layout, reason)`** reports committed changes.
13
+ - **New saved format, `LayoutJSON`** (`version: 2`): readable names, explicit directions, and tab ids as the stable identity.
14
+ - **Old layouts:** v1 trees load automatically, or convert them with `migrateLayoutFromV1`. They render pixel-identically, as checked against layouts recorded from v1.
15
+ - **Deterministic ids**, safe for server rendering; no `crypto.randomUUID()`, which threw outside secure contexts.
16
+ - **Rects are relative to the layout root.** v1 misplaced layouts that weren't at the viewport's top-left.
17
+ - **No import-time side effects:** the console banner and `window.__DYNAMIX_LAYOUT__` are gone. Use `version` instead.
18
+ - **Fixed:**
19
+ - nested rows left behind after some moves;
20
+ - stale sizes when every open panel had weight 0;
21
+ - a row stuck at its minimum next to a folded tabset;
22
+ - the root staying oversized after the container shrank;
23
+ - center drops inserting before the last tab (they now append).
24
+ - **Packaging:** the CommonJS build loads with `require()` (it was ESM under a `.cjs.js` name), exports maps give each condition its own types, and the package installs on Node 18 and later (it required Node 22).
25
+
26
+ **Breaking:**
27
+ - `DynamixLayoutCore`, `Node`, `Bond`, `Node.cache`, `Queue`, `createReactiveState`, the comparators, the drop-preview and DOM helpers, and the `LayoutTree`/`NodeOptions` types are removed.
28
+ - `updateTree(src, des, area)` becomes `moveTab`/`moveTabset(id, target)`, with `'contain'` renamed to `'center'`.
29
+ - `updateDimension` becomes `setContainerRect`, and the debounce arguments are gone; throttle in the adapter instead.
30
+
31
+ ## 1.2.0
32
+
33
+ ### Minor Changes
34
+
35
+ - [#83](https://github.com/xcode-studio/dynamix-layout/pull/83) [`4f01ca4`](https://github.com/xcode-studio/dynamix-layout/commit/4f01ca47e0ec6c28e68b157b2b2bed1522b92d0f) Thanks [@akash-aman](https://github.com/akash-aman)! - Maximize and fold tabsets (engine):
36
+
37
+ - `maximize(id)`, `restore()`, `toggleMaximize(id)` and `maximizedId`: one tabset fills the whole layout; the others are hidden but stay mounted, and restoring brings back the exact previous split.
38
+ - `collapse(id)`, `expand(id)`, `toggleCollapse(id)`: a tabset folds to a strip along its row (its tab bar). Siblings share the freed space, unfolding restores the previous size, and folding the last open child of a row unfolds its most recently folded sibling.
39
+ - New `collapsedSize` option (defaults to `minH`); pass the tab bar height.
40
+ - Computed options expose `nodFold`, `nodMaxd`, `nodHidden`, `nodLocked`, `nodFoldable` and `nodMaximizable`; saved layouts keep `nodFold` (tabsets) and `nodMaxd` (root).
41
+ - Bonds next to a folded tabset are locked; any drag-and-drop move ends maximize; a dragged or drop-target tabset unfolds.
42
+ - Fix: a layout created from a single tab now fills the whole area (an empty row used to take half of it).
43
+
3
44
  ## 1.1.1
4
45
 
5
46
  ### Patch Changes
package/README.md CHANGED
@@ -1,207 +1,38 @@
1
- <div align="center">
1
+ # @dynamix-layout/core
2
2
 
3
- [![Patreon](https://img.shields.io/badge/Patreon-Support-F96854?style=for-the-badge&logo=patreon)](https://www.patreon.com/akashaman)
4
- [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Donate-FFDD00?style=for-the-badge&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/akashaman)
5
- [![Hire Me](https://img.shields.io/badge/Hire%20Me-Email-blue?style=for-the-badge&logo=gmail)](mailto:sir.akashaman@gmail.com)
3
+ The framework-agnostic engine behind Dynamix Layout. It handles the layout tree (rows, tabsets and tabs), geometry and minimum sizes, splitters, drag-and-drop targets, maximize and fold, and versioned serialization. It has no dependencies, no global state and no DOM access. Every `createLayout()` is an independent instance.
6
4
 
7
- </div>
5
+ Use it directly to build an adapter for another framework, or through [`@dynamix-layout/react`](https://www.npmjs.com/package/@dynamix-layout/react) or [`@dynamix-layout/solid`](https://www.npmjs.com/package/@dynamix-layout/solid).
8
6
 
9
- # 🚀 @dynamix-layout/core
7
+ ## Install
10
8
 
11
- ## Overview
12
-
13
- **@dynamix-layout/core** is the **engine** that powers the **creation**, **management**, and **dynamic modification** of your layout structures. It handles the **internal logic** of the layout tree, including **node creation**, **dimension calculation**, and **state updates**. This package works with a **hierarchical tree structure** composed of `Node` objects and exposes a **reactive state** for seamless integration with modern UI frameworks.
14
-
15
- ---
16
-
17
- ### Made with ❤️ by [Akash Aman](https://linktr.ee/akash_aman)
18
-
19
- ---
20
-
21
- ![alt text](https://raw.githubusercontent.com/xcode-studio/dynamix-layout/main/assets/demo1.gif)
22
-
23
- ---
24
-
25
- ## Core Classes
26
-
27
- The package is built around three primary classes: `DynamixLayoutCore`, `Node`, and `Bond`.
28
-
29
- 1. **`DynamixLayoutCore`**: The main controller class that you instantiate to create and manage the entire layout. It serves as the primary public API.
30
- 2. **`Node`**: The fundamental building block of the layout tree. Each `Node` can be a `row`, a `tabset`, or a `tab`.
31
- 3. **`Bond`**: A special object that represents the draggable separator between two nodes, allowing for resizing.
32
-
33
- ---
34
-
35
- ## `DynamixLayoutCore` Class
36
-
37
- The `DynamixLayoutCore` class is the central point of control for your entire layout configuration.
38
-
39
- ### Constructor
40
-
41
- When initializing the `DynamixLayoutCore` class, you provide a single **options object**.
42
-
43
- `new DynamixLayoutCore(options)`
44
-
45
- - **`options`** (`object`): An object containing configuration properties.
46
- - **`tabs`** (`string[]`): An array of tab names to generate an initial binary tree layout. Defaults to `[]`.
47
- - **`tree`** (`LayoutTree | null`): A serialized JSON representation of a previously saved layout (`LayoutTree` object). If provided, it will recreate that specific layout. Defaults to `null`.
48
- - **`tabsIds`** (`Map<string, string>`): A map where keys are tab names and values are their unique IDs. This is useful for persisting tab identities across sessions.
49
- - **`minW`** (`number`): The minimum width for a `tabset`. Defaults to `40`.
50
- - **`minH`** (`number`): The minimum height for a `tabset`. Defaults to `40`.
51
- - **`bond`** (`number`): The width or height (in pixels) of the draggable separator (`Bond`) between nodes. Defaults to `10`.
52
-
53
- ### Reactive Properties
54
-
55
- The layout's state is exposed through static reactive properties on the `Node` class. You can subscribe to these to drive your UI.
56
-
57
- - **`Node.cache.nodOpts`**: A reactive map of all `tabset` nodes and their options.
58
- - **`Node.cache.bndOpts`**: A reactive map of all `bond` objects.
59
- - **`Node.cache.tabOpts`**: A reactive map of all `tab` nodes within their respective tabsets.
60
-
61
- ### Methods
62
-
63
- #### 🌳 `updateTree(src, des, layout)`
64
-
65
- This is the core method for rearranging the layout. It moves a source node (`src`) to a new position relative to a destination node (`des`).
66
-
67
- - **`src`** (`string`): The unique ID of the node to move (can be a `tab` or a `tabset`).
68
- - **`des`** (`string`): The unique ID of the destination node.
69
- - **`layout`** (`'top' | 'bottom' | 'left' | 'right' | 'contain'`): Specifies how the source should be placed relative to the destination.
70
- - `top`, `bottom`, `left`, `right`: Places the source node by splitting the destination area.
71
- - `contain`: Places a source `tab` into the destination `tabset`.
72
-
73
- **Returns**: `boolean` - `true` if the update was successful, `false` otherwise.
74
-
75
- ```javascript
76
- // Moves the node with 'sidebar-id' to be to the left of 'main-content-id'
77
- layout.updateTree('sidebar-id', 'main-content-id', 'left')
9
+ ```bash
10
+ npm install @dynamix-layout/core
78
11
  ```
79
12
 
80
- ---
13
+ ## Quick start
81
14
 
82
- #### 📏 `updateDimension(dim, disableTimeout, timeout)`
15
+ ```ts
16
+ import { createLayout } from '@dynamix-layout/core'
83
17
 
84
- Recalculates all node dimensions within the layout, typically after the main container has been resized.
85
-
86
- - **`dim`** (`{ w, h, x, y }`): An object representing the new dimensions of the root container.
87
- - **`disableTimeout`** (`boolean`): If `true`, the update runs instantly. Otherwise, it's debounced. Defaults to `false`.
88
- - **`timeout`** (`number`): The debounce delay in milliseconds. Defaults to `2`.
89
-
90
- <!-- end list -->
91
-
92
- ```javascript
93
- // On window resize, update the layout dimensions
94
- layout.updateDimension({
95
- w: window.innerWidth,
96
- h: window.innerHeight,
97
- x: 0,
98
- y: 0,
18
+ const layout = createLayout({
19
+ tabs: [{ id: 'editor' }, { id: 'terminal' }, { id: 'preview' }],
20
+ onLayoutChange: (json) => localStorage.setItem('layout', JSON.stringify(json)),
99
21
  })
100
- ```
101
-
102
- ---
103
-
104
- #### ↔️ `updateSlider(id, dim, disableTimeout, timeout)`
105
-
106
- Updates the layout in response to a user dragging a `Bond` (slider).
107
-
108
- - **`id`** (`string`): The unique ID of the `Bond` being moved.
109
- - **`dim`** (`{ x, y }`): An object with the new coordinates of the slider.
110
- - **`disableTimeout`** (`boolean`): If `true`, the update runs instantly. Defaults to `false`.
111
- - **`timeout`** (`number`): The debounce delay in milliseconds. Defaults to `2`.
112
22
 
113
- <!-- end list -->
114
-
115
- ```javascript
116
- // When a user drags a separator
117
- layout.updateSlider('bond-123', { x: 450, y: 300 })
118
- ```
119
-
120
- ---
121
-
122
- #### 🗑️ `clearAllCache()`
123
-
124
- Clears all internal caches for nodes, bonds, dimensions, and other layout data. Use this when completely destroying or resetting the layout.
125
-
126
- ```javascript
127
- layout.clearAllCache()
128
- ```
129
-
130
- ---
131
-
132
- ## `Node` Class
133
-
134
- The `Node` represents a single element in the layout tree. You will typically interact with Node data through the reactive properties rather than manipulating `Node` objects directly.
135
-
136
- ### Constructor
137
-
138
- `new Node(options)`
139
-
140
- - **`options`** (`object`): An object containing node properties like `type`, `host`, `name`, `part`, `dims`, `unId`, and `open`.
141
-
142
- ### Static Cache (`Node.cache`)
143
-
144
- The `Node` class contains a static `cache` object. This centralized cache holds the state for the entire layout, including maps of all nodes, dimensions, and the **reactive options** that your UI will consume. **Direct manipulation of the cache is not recommended.**
145
-
146
- ### Methods
147
-
148
- #### 💾 `toJSON()`
149
-
150
- Serializes the node and its entire subtree into a JSON object (`LayoutTree`). This is used to save the current state of the layout.
151
-
152
- ```javascript
153
- const rootNode = DynamixLayoutCore._root // Access the static root node
154
- const layoutJSON = rootNode.toJSON()
155
- // localStorage.setItem('savedLayout', JSON.stringify(layoutJSON));
156
- ```
157
-
158
- ## Example Usage
159
-
160
- ```javascript
161
- import { DynamixLayoutCore, Node } from '@dynamix-layout/core'
162
-
163
- // 1. Initialize the Layout class
164
- // Provide an options object to the constructor
165
- const layout = new DynamixLayoutCore({
166
- tabs: ['editor', 'terminal', 'preview'],
167
- })
168
-
169
- // ...or restore a layout from a saved JSON object
170
- // const savedLayout = JSON.parse(localStorage.getItem('savedLayout'));
171
- // const layout = new DynamixLayoutCore({ tree: savedLayout });
172
-
173
- // 2. Set the initial dimensions of your container
174
- layout.updateDimension({ w: 1200, h: 800, x: 0, y: 0 }, true)
175
-
176
- // 3. Subscribe to reactive state to render your UI.
177
- const unsubscribe = Node.cache.nodOpts.onChange((nodeOptionsMap) => {
178
- console.log('Layout updated!', nodeOptionsMap)
179
- // Your UI rendering logic here...
23
+ layout.subscribe((snapshot) => {
24
+ for (const [id, rect] of snapshot.rects.tabsets) draw(id, rect)
180
25
  })
181
-
182
- // 4. Move a tab to a new location
183
- // (In a real app, you'd get these IDs from the reactive state)
184
- setTimeout(() => {
185
- const terminalTabId = 'some-unique-id-for-terminal-tab'
186
- const editorTabsetId = 'some-unique-id-for-editor-tabset'
187
-
188
- // Move the terminal tab into the editor's tabset
189
- layout.updateTree(terminalTabId, editorTabsetId, 'contain')
190
- }, 2000)
191
-
192
- // 5. Clean up when the component unmounts
193
- // unsubscribe();
194
- // layout.clearAllCache();
26
+ layout.setContainerRect({ x: 0, y: 0, width: 1200, height: 800 })
27
+ layout.moveTab('terminal', { type: 'root', position: 'bottom' })
195
28
  ```
196
29
 
197
- ---
198
-
199
- <div align="center">
200
-
201
- [![Patreon](https://img.shields.io/badge/Patreon-Support-F96854?style=for-the-badge&logo=patreon)](https://www.patreon.com/akashaman)
202
- [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-Donate-FFDD00?style=for-the-badge&logo=buy-me-a-coffee)](https://www.buymeacoffee.com/akashaman)
203
- [![Hire Me](https://img.shields.io/badge/Hire%20Me-Email-blue?style=for-the-badge&logo=gmail)](mailto:sir.akashaman@gmail.com)
30
+ ## Documentation
204
31
 
205
- ### Made with ❤️ by [Akash Aman](https://linktr.ee/akash_aman)
32
+ - [Using the core without React](https://github.com/xcode-studio/dynamix-layout/blob/main/docs/guides/core-without-react.md)
33
+ - [`createLayout` API](https://github.com/xcode-studio/dynamix-layout/blob/main/docs/api/create-layout.md)
34
+ - [`LayoutJSON`](https://github.com/xcode-studio/dynamix-layout/blob/main/docs/api/layout-json.md)
35
+ - [`migrateLayoutFromV1`](https://github.com/xcode-studio/dynamix-layout/blob/main/docs/api/migrate-layout-from-v1.md)
36
+ - [Migrating from v1](https://github.com/xcode-studio/dynamix-layout/blob/main/docs/migration-v1-to-v2.md)
206
37
 
207
- </div>
38
+ MIT © [Akash Aman](https://linktr.ee/akash_aman)
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ /*! @dynamix-layout/core | MIT License */ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function et(){return typeof process>"u"||process.env.NODE_ENV!=="production"}const jt=t=>{et()&&console.warn(`[dynamix-layout] ${t.message}`)},w=t=>t.type==="row";function E(t){const e=[],n=i=>{w(i)?i.children.forEach(n):e.push(i)};return t.children.forEach(n),e}function Kt(t){return E(t).flatMap(e=>e.children.map(n=>n.id))}function Tt(t){const e=[],n=i=>{e.push(i.id),w(i)&&i.children.forEach(n)};return n(t),e}function I(t,e){return E(t).find(n=>n.id===e)}function S(t,e){return E(t).find(n=>n.children.some(i=>i.id===e))}function it(t,e){for(const n of t.children){if(n.id===e)return t;if(w(n)){const i=it(n,e);if(i)return i}}}const Yt=6;function It(t,e,n,i){if(n.type==="root")return lt(e.container,n.position);if(n.type==="tabset"){const f=e.tabsets.get(n.tabsetId);return f?n.position!=="center"?lt(f,n.position):I(t.root,n.tabsetId)?.isFolded?f:{x:f.x+f.width*.1,y:f.y+f.height*.1,width:f.width*.8,height:f.height*.8}:null}const o=S(t.root,n.tabId),r=o&&i?.tabBars.get(o.id)?.tabs,a=r?.findIndex(f=>f.id===n.tabId)??-1;if(!r||a<0)return null;const d=r.length>1?Math.max(2,r[1].rect.x-(r[0].rect.x+r[0].rect.width)):Yt,{rect:s}=r[a];return{x:n.position==="before"?s.x-d+1:s.x+s.width+1,y:s.y,width:Math.max(2,d-2),height:s.height}}function lt(t,e){const{x:n,y:i,width:o,height:r}=t;switch(e){case"left":return{x:n,y:i,width:o/2,height:r};case"right":return{x:n+o/2,y:i,width:o/2,height:r};case"top":return{x:n,y:i,width:o,height:r/2};case"bottom":return{x:n,y:i+r/2,width:o,height:r/2}}}const Gt=(t,e)=>t.length===e.length&&t.every((n,i)=>n===e[i]);function F(t,e){return Gt(t.children,e)?t:{...t,children:e}}function O(t,e,n){if(t.id===e){const o=n(t);if(Array.isArray(o)||!w(o))throw new Error("The root can only be replaced by a row.");return o}const i=o=>{const r=[];for(const a of o.children)if(a.id===e){const d=n(a);Array.isArray(d)?r.push(...d):r.push(d)}else r.push(w(a)?i(a):a);return F(o,r)};return i(t)}const $=100,zt=t=>Number.isFinite(t)&&t>=0?t:$;function Ht(t){if(t.children.length===0)return null;const e=zt(t.weight),n=t.children.some(i=>i.id===t.activeTabId);return e===t.weight&&n?t:{...t,weight:e,activeTabId:n?t.activeTabId:t.children[0].id}}function St(t){const e=[];for(const o of t.children){if(!w(o)){const a=Ht(o);a&&e.push(a);continue}let r=St(o);w(r)&&r.children.length===1&&(r=r.children[0]),!(w(r)&&r.children.length===0)&&(w(r)&&r.direction===t.direction?e.push(...r.children):e.push(r))}const n=zt(t.weight),i=F(t,e);return n===t.weight?i:{...i,weight:n}}function V(t){let e=St(t);for(;e.children.length===1&&w(e.children[0]);){const i=e.children[0];e={...e,weight:i.weight,direction:i.direction,children:i.children}}return(e.children.length<=1||e.direction==="horizontal")&&(e.direction!=="horizontal"||e.weight!==$)&&(e={...e,direction:"horizontal",weight:$}),e}const Jt=t=>t==="horizontal"?"vertical":"horizontal";function _(t,e){return{type:"tabset",id:e("tabset",t),weight:$,activeTabId:t,isFolded:!1,children:[{type:"tab",id:t}]}}function kt(t,e,n="root"){const i=(o,r,a)=>{const d=o.length<=2?o.map(s=>_(s,e)):[_(o[0],e),i(o.slice(1),Jt(r),e("row"))];return{type:"row",id:a,weight:$,direction:r,children:d}};return i(t,"horizontal",n)}const vt=t=>t==="horizontal"?"vertical":"horizontal",q=t=>t==="top"||t==="left",At=(t,e)=>(t==="left"||t==="right")==(e==="horizontal");function j(t,e,n,i,o){return O(t,e,r=>{const a=r,d=[...a.children];return d.splice(i,0,...n),{...a,children:d,activeTabId:o,isFolded:!1}})}function Dt(t,e,n,i,o){const r=it(t,e),a=I(t,e);if(!r||!a)return t;if(At(i,r.direction)){const f=r.children.indexOf(a)+(q(i)?0:1),p=[...r.children];return p.splice(f,0,n),O(t,r.id,()=>F(r,p))}const d={...a,weight:$,isFolded:!1},s={type:"row",id:o("row"),weight:a.weight,direction:vt(r.direction),children:q(i)?[n,d]:[d,n]};return O(t,e,()=>s)}function Ot(t,e,n,i){const o=q(n);if(t.children.length===0)return F(t,[e]);if(At(n,t.direction))return F(t,o?[e,...t.children]:[...t.children,e]);const r=t.children.length===1?t.children[0]:{type:"row",id:i("row"),weight:t.weight,direction:t.direction,children:t.children};return{...t,weight:$,direction:vt(t.direction),children:o?[e,r]:[r,e]}}function nt(t,e){const n=S(t,e);if(!n)return t;const i=n.children.filter(r=>r.id!==e),o=O(t,n.id,()=>i.length===0?[]:{...n,children:i,activeTabId:n.activeTabId===e?i[0].id:n.activeTabId});return V(o)}function qt(t,e){return V(O(t,e,()=>[]))}const ft=(t,e)=>t.foldOrder.indexOf(e)+1;function Et(t,e,n){let i=null;for(const o of e)o.id===n||w(o)||!o.isFolded||(!i||ft(t,o.id)>ft(t,i.id))&&(i=o);return e.map(o=>o===i?{...i,isFolded:!1}:o)}const Ft=t=>!w(t)&&t.isFolded;function Zt(t){return E(t).length>1}function R(t){const e=d=>{let s=d.children;return s.length>0&&s.every(Ft)&&(s=Et(t,s)),s=s.map(f=>w(f)?e(f):f.isFolded&&s.length<2?{...f,isFolded:!1}:f),F(d,s)},n=e(t.root),i=new Set(Tt(n)),o=t.maximizedTabsetId&&I(n,t.maximizedTabsetId)?t.maximizedTabsetId:null,r=t.foldOrder.filter(d=>i.has(d)),a=t.lastActiveTabsetId&&I(n,t.lastActiveTabsetId)?t.lastActiveTabsetId:null;return n===t.root&&o===t.maximizedTabsetId&&r.length===t.foldOrder.length&&a===t.lastActiveTabsetId?t:{root:n,maximizedTabsetId:o,foldOrder:r,lastActiveTabsetId:a}}function Xt(t,e){return t.maximizedTabsetId===e||!I(t.root,e)||!Zt(t.root)?null:{...t,maximizedTabsetId:e}}function Nt(t){return t.maximizedTabsetId?{...t,maximizedTabsetId:null}:null}function Qt(t,e){const n=I(t.root,e),i=it(t.root,e);if(!n||!i||n.isFolded||i.children.length<2)return null;const o=[...t.foldOrder.filter(s=>s!==e),e],r={...t,foldOrder:o,maximizedTabsetId:null};let a=i.children.map(s=>s===n?{...n,isFolded:!0}:s);a.every(Ft)&&(a=Et(r,a,e));const d=O(t.root,i.id,()=>F(i,a));return R({...r,root:d})}function te(t,e){const n=I(t.root,e);if(!n||!n.isFolded)return null;const i=O(t.root,e,()=>({...n,isFolded:!1}));return R({...t,root:i})}function $t(t,e){return e.type==="tab"?S(t,e.tabId):I(t,e.tabsetId)}function rt(t,e,n){const i=$t(t,e);if(!i||E(t).length===1&&(e.type==="tabset"||i.children.length===1))return!1;switch(n.type){case"root":return!0;case"tab":{const a=S(t,n.tabId);return a?e.type==="tab"?e.tabId!==n.tabId:a!==i:!1}case"tabset":{const a=I(t,n.tabsetId);return a?e.type==="tabset"?a!==i:a!==i?!0:i.children.length<2?!1:n.position!=="center"||i.children[i.children.length-1].id!==e.tabId:!1}}}function Z(t,e,n,i){if(!rt(t.root,e,n))return null;const o=$t(t.root,e),r=e.type==="tab"?[{type:"tab",id:e.tabId}]:o.children,a=e.type==="tab"?e.tabId:r[r.length-1].id,d=()=>e.type==="tab"?_(e.tabId,i):{...o,isFolded:!1};let s=e.type==="tab"?nt(t.root,e.tabId):qt(t.root,o.id);switch(n.type){case"tab":{const f=S(s,n.tabId),p=f.children.findIndex(c=>c.id===n.tabId)+(n.position==="after"?1:0);s=j(s,f.id,r,p,a);break}case"tabset":{if(n.position==="center"){const f=I(s,n.tabsetId);s=j(s,f.id,r,f.children.length,a)}else s=Dt(s,n.tabsetId,d(),n.position,i);break}case"root":s=Ot(s,d(),n.position,i);break}return s=V(s),R({...t,root:s,maximizedTabsetId:null,lastActiveTabsetId:S(s,a)?.id??null})}const ee=["left","right","top","bottom"],G=(t,e)=>e.x>=t.x&&e.x<=t.x+t.width&&e.y>=t.y&&e.y<=t.y+t.height;function Mt(t,e,n={}){const{length:i=.25,thickness:o=8}=n,{x:r,y:a,width:d,height:s}=t;switch(e){case"left":return{x:r,y:a+s*(1-i)/2,width:o,height:s*i};case"right":return{x:r+d-o,y:a+s*(1-i)/2,width:o,height:s*i};case"top":return{x:r+d*(1-i)/2,y:a,width:d*i,height:o};case"bottom":return{x:r+d*(1-i)/2,y:a+s-o,width:d*i,height:o}}}function ie(t,e){const n=e.x-t.x,i=e.y-t.y;return n<t.width/3?"left":n>2*t.width/3?"right":i<t.height/3?"top":i>2*t.height/3?"bottom":"center"}function ne(t,e){if(t.length===0)return null;const n=t[t.length-1];if(e.x>n.rect.x+n.rect.width)return{type:"tab",tabId:n.id,position:"after"};if(e.x<t[0].rect.x)return{type:"tab",tabId:t[0].id,position:"before"};let i=t[0],o=1/0;for(const a of t){const d=a.rect.x+a.rect.width/2;Math.abs(e.x-d)<o&&(o=Math.abs(e.x-d),i=a)}const r=i.rect.x+i.rect.width/2;return{type:"tab",tabId:i.id,position:e.x<r?"before":"after"}}function Rt(t,e,n,i,o,r){const a=s=>s&&rt(t.root,i,s)?s:null;for(const s of ee)if(G(Mt(e.container,s,r),n))return a({type:"root",position:s});const d=E(t.root);for(const s of d){const f=o?.tabBars.get(s.id);if(!(!f||!G(f.rect,n)))return f.isRotated||s.isFolded?a({type:"tabset",tabsetId:s.id,position:"center"}):a(ne(f.tabs,n))}for(const s of d){if(t.maximizedTabsetId&&t.maximizedTabsetId!==s.id)continue;const f=e.tabsets.get(s.id);if(!f||!G(f,n))continue;const p=s.isFolded?"center":ie(f,n);return a({type:"tabset",tabsetId:s.id,position:p})}return null}function ot(t,e){const n=new Map,{width:i,height:o}=e.minPanelSize,r=(a,d)=>{let s;if(w(a)){let f=0,p=0,c=0,l=0;for(const u of a.children){const h=r(u,a.direction);c+=h.horizontalCount,l+=h.verticalCount,a.direction==="horizontal"?(f+=h.width,p=Math.max(p,h.height)):(p+=h.height,f=Math.max(f,h.width))}const b=(a.children.length-1)*e.splitterSize;a.direction==="horizontal"?f+=b:p+=b,s={width:f,height:p,horizontalCount:c,verticalCount:l}}else{const f=d==="horizontal";s=a.isFolded?f?{width:e.foldedSize,height:o,horizontalCount:0,verticalCount:0}:{width:i,height:e.foldedSize,horizontalCount:0,verticalCount:0}:{width:i,height:o,horizontalCount:f?1:0,verticalCount:f?0:1}}return n.set(a.id,s),s};return r(t,t.direction),n}function re(t,e){const n=(i,o)=>i*o+Math.max(0,i-1)*e.splitterSize;return{width:Math.max(t.width,n(t.horizontalCount,e.minPanelSize.width)),height:Math.max(t.height,n(t.verticalCount,e.minPanelSize.height))}}const st=(t,e)=>`${t}~${e}`,H=(t,e)=>{if(t.size!==e.size)return!1;const n=e.entries();for(const[i,o]of t){const[r,a]=n.next().value;if(i!==r||o!==a)return!1}return!0},ht=(t,e)=>!!t&&t.x===e.x&&t.y===e.y&&t.width===e.width&&t.height===e.height;function ut(t,e,n,i,o){const r=ot(t,n),a=re(r.get(t.id),n),d={x:e.x,y:e.y,width:Math.max(e.width,a.width),height:Math.max(e.height,a.height)},s=new Map,f=new Map,p=new Map,c=(u,h,g,m)=>u.set(g,ht(h?.get(g),m)?h.get(g):m),l=(u,h)=>{c(s,o?.rows,u.id,h);const g=u.direction==="horizontal",m=r.get(u.id),y=g?h.width-m.width:h.height-m.height,x=y<0;let v=0,A,C=0;for(const T of u.children)!w(T)&&T.isFolded||(v+=T.weight,A=T.id,C++);const N=v===0,L=T=>N?1:T;N&&(v=C);let P=0,B=0,U=0;u.children.forEach((T,Ut)=>{const ct=r.get(T.id),Wt=w(T)||!T.isFolded;let Y=0;if(!x&&Wt&&v>0){B+=L(T.weight);const W=T.id===A?y:Math.round(y*B/v);Y=W-U,U=W}const z=g?{x:h.x+P,y:h.y,width:ct.width+Y,height:h.height}:{x:h.x,y:h.y+P,width:h.width,height:ct.height+Y};P+=(g?z.width:z.height)+n.splitterSize,w(T)?l(T,z):c(f,o?.tabsets,T.id,z);const dt=u.children[Ut+1];if(dt){const W=g?{x:z.x+z.width,y:z.y,width:n.splitterSize,height:z.height}:{x:z.x,y:z.y+z.height,width:z.width,height:n.splitterSize};c(p,o?.splitters,st(T.id,dt.id),W)}})};l(t,d),i&&f.has(i)&&c(f,o?.tabsets,i,d);const b=ht(o?.container,d)?o.container:d;return o&&b===o.container&&H(s,o.rows)&&H(f,o.tabsets)&&H(p,o.splitters)?o:{container:b,rows:s,tabsets:f,splitters:p}}function Lt(t,e){const n=i=>{for(let o=0;o<i.children.length;o++){const r=i.children[o],a=i.children[o+1];if(a&&st(r.id,a.id)===e)return{row:i,before:r,after:a};if(w(r)){const d=n(r);if(d)return d}}return null};return n(t)}const bt=t=>!w(t)&&t.isFolded;function oe(t){return bt(t.before)||bt(t.after)}const K=(t,e)=>(w(e)?t.rows:t.tabsets).get(e.id);function se(t,e,n,i){const o=Lt(t,i);if(!o)return null;const r=K(e,o.before),a=K(e,o.after);if(!r||!a)return null;const d=ot(t,n),s=o.row.direction==="horizontal",f=c=>s?c.width:c.height,p=c=>{const l=d.get(c.id);return s?l.width:l.height};return{value:f(r),min:p(o.before),max:f(r)+f(a)-p(o.after)}}function Ct(t,e,n,i,o){const r=Lt(t,i);if(!r||oe(r))return null;const a=K(e,r.before),d=K(e,r.after);if(!a||!d)return null;const s=ot(t,n),f=r.row.direction==="horizontal",p=f?a.x:a.y,c=f?d.x+d.width:d.y+d.height,l=s.get(r.before.id),b=s.get(r.after.id),u=f?l.width:l.height,h=f?b.width:b.height,g=n.splitterSize/2,m=f?o.x:o.y,y=Math.max(p+u+g,Math.min(c-h-g,m)),x=y-g-p,v=c-(y+g),A=Math.max(0,x-u),C=Math.max(0,v-h),N=A+C,L=r.before.weight+r.after.weight,P=N>0?A/N*L:L/2,B=N>0?C/N*L:L/2,U=r.row.children.map(T=>T===r.before?{...T,weight:P}:T===r.after?{...T,weight:B}:T);return O(t,r.row.id,()=>F(r.row,U))}const ae=t=>({type:"tabset",id:t.id,weight:t.weight,activeTabId:t.activeTabId,...t.isFolded?{isFolded:!0}:{},children:t.children.map(e=>({type:"tab",id:e.id}))}),Pt=t=>({type:"row",id:t.id,weight:t.weight,direction:t.direction,children:t.children.map(e=>w(e)?Pt(e):ae(e))});function _t(t){return{version:2,root:Pt(t.root),...t.maximizedTabsetId?{maximizedTabsetId:t.maximizedTabsetId}:{}}}const ce=(t,e)=>{for(const n of Object.keys(t)){const i=t[n],o=e[n];if(Array.isArray(i)&&Array.isArray(o)){if(i.length!==o.length||i.some((r,a)=>r!==o[a]))return!1}else if(i!==o)return!1}return!0},X=t=>et()?Object.freeze(t):t;function J(t,e){if(!e)return t.forEach(r=>X(r)),t;let n=t.size!==e.size;const i=[...e.keys()];let o=0;for(const[r,a]of t){const d=e.get(r);d&&ce(d,a)?t.set(r,d):(X(a),n=!0),i[o++]!==r&&(n=!0)}return n?t:e}function gt(t,e,n,i){if(i&&i.root===t.root&&i.maximizedTabsetId===t.maximizedTabsetId&&i.rects===e&&i.drag===n)return i;const o=new Map,r=new Map,a=new Map,d=t.maximizedTabsetId;let s=0;const f=c=>c.children.forEach(l=>w(l)?f(l):s++);f(t.root);const p=c=>{c.children.forEach((l,b)=>{if(w(l))p(l);else{const h=l.id===d,g=!!d&&!h,m=l.isFolded&&!h;o.set(l.id,{id:l.id,tabIds:l.children.map(y=>y.id),activeTabId:l.activeTabId,parentDirection:c.direction,isFolded:m,isMaximized:h,isHidden:g,canFold:c.children.length>1,canMaximize:s>1});for(const y of l.children){const x=y.id===l.activeTabId;a.set(y.id,{id:y.id,tabsetId:l.id,isActive:x,isVisible:x&&!m&&!g})}}const u=c.children[b+1];if(u){const h=st(l.id,u.id),g=m=>!w(m)&&m.isFolded;r.set(h,{id:h,rowId:c.id,direction:c.direction,beforeId:l.id,afterId:u.id,isLocked:g(l)||g(u),isHidden:!!d})}})};return p(t.root),X({root:t.root,tabsets:J(o,i?.tabsets),splitters:J(r,i?.splitters),tabs:J(a,i?.tabs),rects:e,maximizedTabsetId:d,drag:n})}function de(t,e,n,i){const o=new Set;let r=t,a=e,d={x:0,y:0,width:0,height:0},s=ut(r.root,d,a,r.maximizedTabsetId),f=null,p=gt(r,s,null),c=!1,l=null;const b=()=>{s=ut(r.root,d,a,r.maximizedTabsetId,s);const h=fe(p.drag,f)?p.drag:f&&le(f),g=gt(r,s,h,p);g!==p&&(p=g,o.forEach(m=>m(p)))},u=()=>(l&&l.model.root===r.root&&l.model.maximizedTabsetId===r.maximizedTabsetId||(l={model:r,value:_t(r)}),l.value);return{get model(){return r},get rects(){return s},get config(){return a},get snapshot(){return p},get drag(){return f},get isDestroyed(){return c},subscribe(h){return o.add(h),()=>o.delete(h)},setContainer(h){d=h,c||b()},setConfig(h){a=h,c||b()},update(h){c||(h.model&&(r=h.model),h.drag!==void 0&&(f=h.drag),b())},commit(h,g){return c?(i({code:"DESTROYED",message:"This layout was destroyed; the call was ignored."}),!1):!h||h===r?!1:(r=h,b(),g&&n?.(u(),g),!0)},report(h){c||n?.(u(),h)},toJSON:u,destroy(){c=!0,f=null,o.clear()}}}const le=({source:t,target:e,indicator:n})=>({source:t,target:e,indicator:n});function fe(t,e){return!t||!e?t===e:t.source===e.source&&t.target===e.target&&t.indicator===e.indicator}function he(t,e){return t===e?!0:!t||!e||t.type!==e.type||t.position!==e.position?!1:t.type==="tab"?t.tabId===e.tabId:t.type==="tabset"?t.tabsetId===e.tabsetId:!0}function ue(t,e){const n={startDrag(i){if(t.isDestroyed)return!1;const{snapshot:o,model:r}=t;if(i.type==="splitter"){const a=o.splitters.get(i.splitterId);if(!a||a.isLocked||r.maximizedTabsetId)return!1}else{if(!(i.type==="tab"?o.tabs.has(i.tabId):o.tabsets.has(i.tabsetId)))return!1;t.commit(Nt(r),"maximize")}return t.update({drag:{source:i,target:null,indicator:null,startRoot:t.model.root}}),!0},updateDrag(i,o){const{drag:r,model:a,rects:d,config:s}=t;if(r){if(r.source.type==="splitter"){const f=Ct(a.root,d,s,r.source.splitterId,i);f&&t.update({model:{...a,root:f}});return}n.setDragTarget(Rt(a,d,i,r.source,o),o)}},setDragTarget(i,o){const{drag:r,model:a,rects:d}=t;if(!r||r.source.type==="splitter"||he(r.target,i))return;const s=i&&It(a,d,i,o);t.update({drag:{...r,target:i,indicator:s}})},endDrag(){const{drag:i,model:o}=t;if(!i)return!1;if(t.update({drag:null}),i.source.type==="splitter")return o.root===i.startRoot?!1:(t.report("resize"),!0);const r=i.target&&Z(o,i.source,i.target,e);return t.commit(r,"move")},cancelDrag(){const{drag:i,model:o}=t;if(!i)return;const r=i.source.type==="splitter"&&o.root!==i.startRoot;t.update({drag:null,...r?{model:{...o,root:i.startRoot}}:{}})}};return n}const be=["center","left","right","top","bottom"],ge=["left","right","top","bottom"];function pe(t,e){const n=[];for(const i of E(t.root))for(const o of be)n.push({type:"tabset",tabsetId:i.id,position:o});for(const i of ge)n.push({type:"root",position:i});return n.filter(i=>rt(t.root,e,i))}class M extends Error{code;path;constructor(e,n,i){super(i?`${n} (at ${i})`:n),this.name="DynamixLayoutError",this.code=e,this.path=i}}const me={minPanelSize:{width:40,height:40},splitterSize:10,foldedSize:40},pt={row:"row",tabset:"ts"};function ye(t){const e=new Set;let n=0;return{createId:(o,r)=>{if(t){const d=t(o,r);return e.add(d),d}let a=r===void 0?"":`${pt[o]}-${r}`;for(;a===""||e.has(a);)a=`${pt[o]}-${++n}`;return e.add(a),a},reserve(o){for(const r of o)e.add(r)},reset(){e.clear(),n=0}}}const k=t=>typeof t=="object"&&t!==null&&!Array.isArray(t),D=(t,e)=>{throw new M("INVALID_LAYOUT",t,e)},we=["horizontal","vertical"];function Q(t,e){const n=new Set,i=new Set,o=(c,l)=>{(typeof c.id!="string"||c.id==="")&&D("Expected a non-empty string id",`${l}.id`);const b=c.id;return n.has(b)&&D(`Duplicate node id "${b}"`,`${l}.id`),n.add(b),b},r=(c,l)=>{const b=c.weight;return typeof b=="number"&&Number.isFinite(b)&&b>=0?b:(e({code:"INVALID_WEIGHT",message:"Invalid weight replaced by 100",path:`${l}.weight`}),100)},a=(c,l)=>{const b=o(c,l);Array.isArray(c.children)||D("Expected a children array",`${l}.children`);const u=c.children.flatMap((g,m)=>{const y=`${l}.children[${m}]`;(!k(g)||g.type!=="tab"||typeof g.id!="string"||g.id==="")&&D('Expected { type: "tab", id: string }',y);const x=g.id;return i.has(x)?(e({code:"DUPLICATE_TAB_ID",message:`Duplicate tab "${x}" dropped`,path:y}),[]):(i.add(x),[{type:"tab",id:x}])}),h=c.activeTabId;return h!==void 0&&!u.some(g=>g.id===h)&&e({code:"INVALID_ACTIVE_TAB",message:`Active tab "${String(h)}" is not in the tabset`,path:`${l}.activeTabId`}),{type:"tabset",id:b,weight:r(c,l),activeTabId:typeof h=="string"?h:u[0]?.id??"",isFolded:c.isFolded===!0,children:u}},d=(c,l)=>{const b=o(c,l);we.includes(c.direction)||D('Expected direction "horizontal" or "vertical"',`${l}.direction`),Array.isArray(c.children)||D("Expected a children array",`${l}.children`);const u=c.children.map((h,g)=>{const m=`${l}.children[${g}]`;return k(h)?h.type==="row"?d(h,m):h.type==="tabset"?a(h,m):D('Expected type "row" or "tabset"',`${m}.type`):D("Expected a row or tabset",m)});return{type:"row",id:b,weight:r(c,l),direction:c.direction,children:u}};(!k(t.root)||t.root.type!=="row")&&D("Expected a root row","root");const s=V(d(t.root,"root")),f=typeof t.maximizedTabsetId=="string"?t.maximizedTabsetId:null,p=R({root:s,maximizedTabsetId:f,lastActiveTabsetId:null,foldOrder:[]});return{root:p.root,maximizedTabsetId:p.maximizedTabsetId}}function at(t){return typeof t=="object"&&t!==null&&!("version"in t)&&typeof t.typNode=="string"}const xe=t=>typeof t=="number"&&Number.isFinite(t)&&t>=0;function Vt(t,e={}){const n=e.onWarning??(()=>{}),i=(u,h)=>xe(u.nodPart)?u.nodPart:(n({code:"INVALID_WEIGHT",message:"Invalid weight replaced by 100",path:`${h}.nodPart`}),100);if(!at(t))throw new M("INVALID_LAYOUT","Not a v1 layout","");const o=new Set,r=u=>{typeof u.uidNode=="string"&&o.add(u.uidNode),u.nodKids?.forEach(r)};r(t);const a=u=>{let h=u;for(let g=1;o.has(h);g++)h=`${u}-${g}`;return o.add(h),h},d=(u,h)=>typeof u.uidNode=="string"&&u.uidNode!==""?u.uidNode:a(h),s=new Set,f=(u,h)=>u.flatMap((g,m)=>{if(g?.typNode!=="tab")return[];const y=g.nodName;return typeof y!="string"||y===""||s.has(y)?(n({code:"DUPLICATE_TAB_ID",message:`Duplicate or unnamed tab "${String(y)}" dropped`,path:`${h}.nodKids[${m}]`}),[]):(s.add(y),[{type:"tab",id:y}])}),p=(u,h)=>{const g=f(u.nodKids??[],h),m=typeof u.nodOpen=="string"?u.nodOpen:"",y=g.some(x=>x.id===m);return m&&!y&&g.length>0&&n({code:"INVALID_ACTIVE_TAB",message:`Active tab "${m}" is not in the tabset`,path:`${h}.nodOpen`}),{type:"tabset",id:d(u,"ts"),weight:i(u,h),...g.length>0?{activeTabId:y?m:g[0].id}:{},...u.nodFold===!0?{isFolded:!0}:{},children:g}},c=(u,h,g)=>{const m=h%2===0?"horizontal":"vertical",y=[];return(u.nodKids??[]).forEach((x,v)=>{const A=`${g}.nodKids[${v}]`;x?.typNode==="row"?y.push(c(x,h+1,A)):x?.typNode==="tabset"?y.push(p(x,A)):x?.typNode==="tab"&&y.push({...p({...x,uidNode:a(`ts-${x.nodName}`),nodKids:[x],nodOpen:x.nodName},A)})}),{type:"row",id:d(u,"row"),weight:i(u,g),direction:m,children:y}},b={root:t.typNode==="row"?c(t,0,"root"):t.typNode==="tabset"?{type:"row",id:a("root"),weight:100,direction:"horizontal",children:[p(t,"root")]}:(()=>{throw new M("INVALID_LAYOUT",`A v1 layout root must be a row, not "${t.typNode}"`,"root")})(),...typeof t.nodMaxd=="string"?{maximizedTabsetId:t.nodMaxd}:{}};return _t(Q(b,n))}const tt=2,Te=t=>typeof t=="object"&&t!==null&&!Array.isArray(t);function Ie(t,e){if(at(t))return e({code:"MIGRATED_FROM_V1",message:"Loaded a v1 layout; save it again to store the v2 format."}),Q(Vt(t,{onWarning:e}),e);if(!Te(t))throw new M("INVALID_LAYOUT","Expected a layout object");if(t.version!==tt)throw new M("UNSUPPORTED_VERSION",`Unsupported layout version ${String(t.version)}; expected ${tt}`,"version");return Q(t,e)}function ze(t,e){return t.lastActiveTabsetId&&I(e,t.lastActiveTabsetId)?t.lastActiveTabsetId:E(e)[0]?.id}function Bt(t,e,n,i=!0){if(S(t.root,e.id))return t;const o={type:"tab",id:e.id},r=e.target;let a=t.root;const d=s=>{const f=I(a,s),p=i?e.id:f.activeTabId;return j(a,s,[o],f.children.length,p)};if(r?.type==="tab"&&S(a,r.tabId)){const s=S(a,r.tabId),f=s.children.findIndex(p=>p.id===r.tabId)+(r.position==="after"?1:0);a=j(a,s.id,[o],f,i?e.id:s.activeTabId)}else r?.type==="tabset"&&I(a,r.tabsetId)?a=r.position==="center"?d(r.tabsetId):Dt(a,r.tabsetId,_(e.id,n),r.position,n):r?.type==="root"||E(a).length===0?a=Ot(a,_(e.id,n),r?.type==="root"?r.position:"right",n):a=d(ze(t,a));return a=V(a),R({...t,root:a,lastActiveTabsetId:i?S(a,e.id)?.id??null:t.lastActiveTabsetId})}function mt(t,e,n,i=!0){const o=new Set(e.map(d=>d.id));let r=t.root;for(const d of Kt(r))o.has(d)||(r=nt(r,d));let a=r===t.root?t:R({...t,root:r});for(const d of e)a=Bt(a,d,n,i);return a}const yt="root";function Se(t){const e=t.onWarning??jt,n=ye(t.createId),{createId:i,reserve:o}=n;let r=xt(t.tabs,e);const a=c=>{const l=Ie(c,e);o(Tt(l.root));const b={...l,lastActiveTabsetId:null,foldOrder:[]};return mt(b,r,i,!1)},d=()=>{if(t.initialLayout)return a(t.initialLayout);o([yt]);const c=r.map(l=>l.id);return{root:kt(c,i,yt),maximizedTabsetId:null,lastActiveTabsetId:null,foldOrder:[]}},s=de(d(),wt(t,me),t.onLayoutChange,e),f=(c,l)=>(e({code:"UNKNOWN_ID",message:`Unknown ${c} "${l}"`}),!1),p={getSnapshot:()=>s.snapshot,subscribe:c=>s.subscribe(c),setContainerRect:c=>s.setContainer(c),setOptions:c=>s.setConfig(wt(c,s.config)),setTabs(c){r=xt(c,e),s.commit(mt(s.model,r,i,!0),"tabs")},moveTab:(c,l)=>s.commit(Z(s.model,{type:"tab",tabId:c},l,i),"move"),moveTabset:(c,l)=>s.commit(Z(s.model,{type:"tabset",tabsetId:c},l,i),"move"),selectTab(c){const{model:l}=s,b=S(l.root,c);if(!b)return f("tab",c);if(b.activeTabId===c&&l.lastActiveTabsetId===b.id)return!1;const u=O(l.root,b.id,()=>({...b,activeTabId:c}));return s.commit({...l,root:u,lastActiveTabsetId:b.id},"select")},addTab(c,l){if(r.some(u=>u.id===c.id))return!1;const b=l?{...c,target:l}:c;return r=[...r,b],s.commit(Bt(s.model,b,i,!0),"tabs")},removeTab(c){if(!r.some(u=>u.id===c))return f("tab",c);r=r.filter(u=>u.id!==c);const{model:l}=s,b=nt(l.root,c);return s.commit(R({...l,root:b}),"tabs")},resizeSplitter(c,l){const{model:b,rects:u,config:h}=s;if(b.maximizedTabsetId)return!1;const g=Ct(b.root,u,h,c,l);return s.commit(g&&{...b,root:g},"resize")},moveSplitterBy(c,l){const b=s.rects.splitters.get(c),u=s.snapshot.splitters.get(c);if(!b||!u)return f("splitter",c);const h=b.x+b.width/2,g=b.y+b.height/2,m=u.direction==="horizontal"?{x:h+l,y:g}:{x:h,y:g+l};return p.resizeSplitter(c,m)},maximize:c=>s.commit(Xt(s.model,c),"maximize"),restore:()=>s.commit(Nt(s.model),"maximize"),toggleMaximize:c=>s.model.maximizedTabsetId===c?p.restore():p.maximize(c),fold:c=>s.commit(Qt(s.model,c),"fold"),unfold:c=>s.commit(te(s.model,c),"fold"),toggleFold(c){const l=s.snapshot.tabsets.get(c);return l?l.isFolded?p.unfold(c):p.fold(c):f("tabset",c)},...ue(s,i),getDropTarget:(c,l,b)=>l.type==="splitter"?null:Rt(s.model,s.rects,c,l,b),getDropIndicatorRect:(c,l)=>It(s.model,s.rects,c,l),listDropTargets:c=>c.type==="splitter"?[]:pe(s.model,c),getSplitterBounds:c=>se(s.model.root,s.rects,s.config,c),load:c=>{s.commit(a(c))},reset(){n.reset(),s.commit(d(),"reset")},toJSON:()=>s.toJSON(),destroy:()=>s.destroy()};return p}function wt(t,e){return{minPanelSize:t.minPanelSize??e.minPanelSize,splitterSize:t.splitterSize??e.splitterSize,foldedSize:t.foldedSize??t.minPanelSize?.height??e.foldedSize}}function xt(t,e){const n=new Set;return t.filter(i=>{if(!n.has(i.id))return n.add(i.id),!0;if(et())throw new M("DUPLICATE_TAB_ID",`Duplicate tab id "${i.id}"`);return e({code:"DUPLICATE_TAB_ID",message:`Duplicate tab id "${i.id}" ignored`}),!1})}function ve(t,e,n){const{x:i,y:o,width:r,height:a}=t;return e.isFolded&&e.parentDirection==="horizontal"?{rect:{x:i+r,y:o,width:a,height:r},isRotated:!0}:{rect:{x:i,y:o,width:r,height:n},isRotated:!1}}function Ae(t,e){return{x:t.x,y:t.y+e,width:t.width,height:Math.max(0,t.height-e)}}function De(t,e){const{style:n}=t;n.left=`${e.x}px`,n.top=`${e.y}px`,n.width=`${e.width}px`,n.height=`${e.height}px`}function Oe(t){let e=null,n=null;const i=()=>{n=null;const o=e;e=null,o&&t(o.value)};return{schedule(o){e={value:o},n===null&&(n=requestAnimationFrame(i))},flush(){n!==null&&(cancelAnimationFrame(n),i())},cancel(){n!==null&&cancelAnimationFrame(n),n=null,e=null}}}const Ee="2.0.0";exports.DynamixLayoutError=M;exports.LAYOUT_VERSION=tt;exports.applyRect=De;exports.createFrameScheduler=Oe;exports.createLayout=Se;exports.getRootDropZoneRect=Mt;exports.getTabBarPlacement=ve;exports.getTabContentRect=Ae;exports.isLayoutV1=at;exports.migrateLayoutFromV1=Vt;exports.version=Ee;