react-zeugma 6.0.4 → 6.0.5

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
@@ -113,24 +113,26 @@ The context provider that sets up the drag-and-drop state machine, monitors acti
113
113
 
114
114
  A custom state hook that initializes and manages the recursive layout tree and handles drag-and-drop actions.
115
115
 
116
- | Option | Type | Default | Description |
117
- | ------------------------ | --------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
118
- | `initialLayout` | `TreeNode \| null` | — | Initial layout tree structure for uncontrolled mode. Only used on initial mount. |
119
- | `layout` | `TreeNode \| null` | — | Controlled layout tree structure. If provided, the hook runs in controlled mode and synchronizes with it. |
120
- | `locked` | `boolean` | `false` | If true, layout resizes and drags are disabled. |
121
- | `dragActivationDistance` | `number` | `8` | Minimum pointer drag distance (in pixels) required to activate dragging. |
122
- | `snapThreshold` | `number` | `8` | Threshold in pixels to snap layout resizers to adjacent edges. |
123
- | `minSplitPercentage` | `number` | `5` | Minimum resizing limit percentage. |
124
- | `maxSplitPercentage` | `number` | `95` | Maximum resizing limit percentage. |
125
- | `enableDragToDismiss` | `boolean` | `false` | If true, enables the drag-out-to-dismiss gesture to close widgets. |
126
- | `dismissThreshold` | `number` | `60` | Distance in pixels outside container bounds required to trigger dismissal. |
127
- | `onRemove` | `(paneId: string) => void` | | Callback triggered when a pane is removed. |
128
- | `onDragStart` | `(activeId: string) => void` | | Callback triggered when dragging starts. |
129
- | `onDragEnd` | `(activeId: string, overId: string \| null, dropAction: any) => void` | — | Callback triggered when dragging ends. |
130
- | `onResizeStart` | `(currentNode: SplitNode) => void` | — | Callback triggered when resizing starts. |
131
- | `onResize` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered during resizing. |
132
- | `onResizeEnd` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered when resizing ends. |
133
- | `onDismissIntentChange` | `(paneId: string \| null) => void` | — | Callback triggered when drag-out intent changes. |
116
+ | Option | Type | Default | Description |
117
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- |
118
+ | `initialLayout` | `TreeNode \| null` | — | Initial layout tree structure for uncontrolled mode. Only used on initial mount. |
119
+ | `layout` | `TreeNode \| null` | — | Controlled layout tree structure. If provided, the hook runs in controlled mode and synchronizes with it. |
120
+ | `fullscreenPaneId` | `string \| null` | | Controlled fullscreen pane ID. Pass `null` for no fullscreen pane. |
121
+ | `onFullscreenChange` | `(paneId: string \| null) => void` | | Callback triggered when a pane is toggled to/from fullscreen mode. |
122
+ | `locked` | `boolean` | `false` | If true, layout resizes and drags are disabled. |
123
+ | `dragActivationDistance` | `number` | `8` | Minimum pointer drag distance (in pixels) required to activate dragging. |
124
+ | `snapThreshold` | `number` | `8` | Threshold in pixels to snap layout resizers to adjacent edges. |
125
+ | `minSplitPercentage` | `number` | `5` | Minimum resizing limit percentage. |
126
+ | `maxSplitPercentage` | `number` | `95` | Maximum resizing limit percentage. |
127
+ | `enableDragToDismiss` | `boolean` | `false` | If true, enables the drag-out-to-dismiss gesture to close widgets. |
128
+ | `dismissThreshold` | `number` | `60` | Distance in pixels outside container bounds required to trigger dismissal. |
129
+ | `onRemove` | `(paneId: string) => void` | — | Callback triggered when a pane is removed. |
130
+ | `onDragStart` | `(activeId: string) => void` | — | Callback triggered when dragging starts. |
131
+ | `onDragEnd` | `(activeId: string, overId: string \| null, dropAction: { type: 'split' \| 'move'; direction?: SplitDirection; position?: string } \| null) => void` | — | Callback triggered when dragging ends, with drop target and action details. |
132
+ | `onResizeStart` | `(currentNode: SplitNode) => void` | — | Callback triggered when resizing starts. |
133
+ | `onResize` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered during resizing. |
134
+ | `onResizeEnd` | `(currentNode: SplitNode, percentage: number) => void` | — | Callback triggered when resizing ends. |
135
+ | `onDismissIntentChange` | `(paneId: string \| null) => void` | — | Callback triggered when drag-out intent changes. |
134
136
 
135
137
  ### `useZeugmaContext()`
136
138
 
@@ -155,36 +157,67 @@ Recursively renders the split nodes and pane nodes. Must be placed inside `<Zeug
155
157
 
156
158
  Wraps the individual pane components inside the renderer. Utilizes a render prop passing active layout attributes.
157
159
 
158
- | Prop | Type | Required | Description |
159
- | ---------- | --------------------------------------- | -------- | ------------------------------------------------------- |
160
- | `id` | `string` | Yes | The unique ID corresponding to a `PaneNode`'s `paneId`. |
161
- | `children` | `(props: PaneRenderProps) => ReactNode` | Yes | Render prop function. |
160
+ | Prop | Type | Required | Description |
161
+ | ---------- | --------------------------------------- | -------- | ---------------------------------------------------------------------- |
162
+ | `id` | `string` | Yes | The unique ID corresponding to a `PaneNode`'s `paneId`. |
163
+ | `children` | `(props: PaneRenderProps) => ReactNode` | Yes | Render prop function. |
164
+ | `style` | `React.CSSProperties` | No | Optional inline CSS styles applied to the pane outer container. |
165
+ | `locked` | `boolean` | No | Optional override to lock this specific pane (disables drag and drop). |
162
166
 
163
167
  #### Render Props: `PaneRenderProps`
164
168
 
165
- | Parameter | Type | Description |
166
- | ------------------ | ----------------- | -------------------------------------------------------------------------------- |
167
- | `isDragging` | `boolean` | Returns `true` if the node wrapper is actively being dragged. |
168
- | `isFullscreen` | `boolean` | Returns `true` if the pane is zoomed/fullscreen. |
169
- | `toggleFullscreen` | `() => void` | Callback to toggle fullscreen viewport coverage. |
170
- | `remove` | `() => void` | Triggers removal of this pane from the layout tree. |
171
- | `renderActiveTab` | `() => ReactNode` | Renders the target placeholder element for the currently active tab in the pane. |
169
+ | Parameter | Type | Description |
170
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
171
+ | `isDragging` | `boolean` | `true` if the pane is actively being dragged. |
172
+ | `isFullscreen` | `boolean` | `true` if the pane currently occupies the fullscreen view. |
173
+ | `toggleFullscreen` | `() => void` | Toggles the pane to and from fullscreen/zoomed mode. |
174
+ | `remove` | `() => void` | Removes this pane (and its active tab) from the layout tree. |
175
+ | `metadata` | `Record<string, unknown> \| undefined` | The metadata values associated with the active tab. |
176
+ | `updateMetadata` | `(updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates metadata of the active tab via an updater function. |
177
+ | `locked` | `boolean` | `true` if this specific pane or the dashboard globally is locked. |
178
+ | `tabs` | `string[]` | The array of tab IDs in this pane. |
179
+ | `activeTabId` | `string` | The currently active tab ID. |
180
+ | `selectTab` | `(tabId: string) => void` | Selects a specific tab to make it active. |
181
+ | `removeTab` | `(tabId: string) => void` | Removes/closes a specific tab. |
182
+ | `tabsMetadata` | `Record<string, Record<string, unknown>> \| undefined` | Metadata values associated with all tabs in this pane. |
183
+ | `updateTabMetadata` | `(tabId: string, updater: (current: Record<string, unknown> \| undefined) => Record<string, unknown> \| undefined) => void` | Updates the metadata of a specific tab. |
184
+ | `renderActiveTab` | `() => ReactNode` | Renders the portal placeholder for the currently active tab in the pane. |
172
185
 
173
186
  ### `<Tabs>`
174
187
 
175
188
  A helper component that renders a list of tab items for a pane, wrapping the internal drag-and-drop tab logic.
176
189
 
177
- | Prop | Type | Required | Description |
178
- | :------------- | :----------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------- |
179
- | `tabs` | `string[]` | Yes | The list of tab IDs in this pane. |
180
- | `activeTabId` | `string` | Yes | The currently active tab ID. |
181
- | `locked` | `boolean` | No | Whether dragging/reordering tabs is disabled (defaults to `false`). |
182
- | `tabsMetadata` | `Record<string, Record<string, any>>` | No | Metadata associated with each tab. |
183
- | `selectTab` | `(id: string) => void` | Yes | Callback when a tab is selected. |
184
- | `removeTab` | `(id: string) => void` | Yes | Callback when a tab is closed/removed. |
185
- | `classNames` | `{ container?: string; tab?: string \| ((tabId: string) => string) }` | No | Custom class names for the container and individual tab items. |
186
- | `styles` | `{ container?: CSSProperties; tab?: CSSProperties \| ((tabId: string) => CSSProperties) }` | No | Custom inline CSS styles for the container and individual tab items. |
187
- | `renderTab` | `(props: { tabId: string; activeTabId: string; isDragging: boolean; isOver: boolean; metadata?: Record<string, any> }) => ReactNode` | Yes | Render prop function called for each tab item. |
190
+ | Prop | Type | Required | Description |
191
+ | :------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------------------------------------------------------------- |
192
+ | `tabs` | `string[]` | Yes | The list of tab IDs in this pane. |
193
+ | `activeTabId` | `string` | Yes | The currently active tab ID. |
194
+ | `locked` | `boolean` | No | Whether dragging/reordering tabs is disabled (defaults to `false`). |
195
+ | `tabsMetadata` | `Record<string, Record<string, any>>` | No | Metadata associated with each tab. |
196
+ | `selectTab` | `(id: string) => void` | Yes | Callback when a tab is selected. |
197
+ | `removeTab` | `(id: string) => void` | Yes | Callback when a tab is closed/removed. |
198
+ | `classNames` | `{ container?: string; tab?: string \| ((tabId: string) => string) }` | No | Custom class names for the container and individual tab items. |
199
+ | `styles` | `{ container?: CSSProperties; tab?: CSSProperties \| ((tabId: string) => CSSProperties) }` | No | Custom inline CSS styles for the container and individual tab items. |
200
+ | `renderTab` | `(props: { tabId: string; activeTabId: string; isDragging: boolean; isOver: boolean; metadata?: Record<string, unknown>; selectTab: (id: string) => void; removeTab: (id: string) => void }) => ReactNode` | Yes | Render prop function called for each tab item. |
201
+
202
+ ### `useTabContext()`
203
+
204
+ A context hook that provides per-tab state from within a `<Tab>` component rendered inside `<Tabs>`. Must be used within a rendered tab child.
205
+
206
+ ```ts
207
+ const { tabId, isActive, isDragging, isOver, metadata, locked, selectTab, removeTab } =
208
+ useTabContext()
209
+ ```
210
+
211
+ | Property | Type | Description |
212
+ | ------------ | -------------------------------------- | -------------------------------------------------- |
213
+ | `tabId` | `string` | The ID of the tab this context belongs to. |
214
+ | `isActive` | `boolean` | Whether this tab is the currently active tab. |
215
+ | `isDragging` | `boolean` | Whether this tab is currently being dragged. |
216
+ | `isOver` | `boolean` | Whether a dragged item is currently over this tab. |
217
+ | `metadata` | `Record<string, unknown> \| undefined` | Custom metadata associated with this tab. |
218
+ | `locked` | `boolean` | Whether this tab (or the dashboard) is locked. |
219
+ | `selectTab` | `() => void` | Selects this tab (no argument needed). |
220
+ | `removeTab` | `() => void` | Removes/closes this tab (no argument needed). |
188
221
 
189
222
  ### `<DragHandle>`
190
223
 
@@ -196,39 +229,57 @@ Defines the interactive drag region inside a `<Pane>`. **Must be placed inside a
196
229
  | `className` | `string` | No | Custom CSS class for the drag handle wrapper. |
197
230
  | `style` | `React.CSSProperties` | No | Inline styles for the drag handle wrapper. |
198
231
 
199
- ### `<ResizableContainer>`
232
+ ## Tree Utilities
200
233
 
201
- A vertical-resize container wrapper that wraps any node (typically `<PaneTree />` or a dashboard component) and allows the user to resize its height by dragging a handle at the bottom edge. Includes smooth scroll parent propagation and drag-to-scroll infinite scrolling behavior.
234
+ Import these serializable tree utility functions from `react-zeugma/utils` for programmatically mutating or querying layout schemas.
202
235
 
203
- | Prop | Type | Required | Default | Description |
204
- | :----------------- | :------------------------- | :------- | :--------------- | :--------------------------------------------------------------- |
205
- | `height` | `number` | No | `400` | Controlled height in pixels (or initial height if uncontrolled). |
206
- | `onHeightChange` | `(height: number) => void` | No | — | Callback function triggered during or after dragging to resize. |
207
- | `minHeight` | `number` | No | `100` | Minimum allowed height in pixels. |
208
- | `maxHeight` | `number` | No | `Infinity` | Maximum allowed height in pixels. |
209
- | `persist` | `boolean` | No | — | Whether to persist height changes in localStorage. |
210
- | `localStorageKey` | `string` | No | `'default-pane'` | Custom localStorage key name (prefixed by `zeugma-height:`). |
211
- | `resizerHeight` | `number` | No | `6` | Height of the resizer drag handle in pixels. |
212
- | `className` | `string` | No | — | Custom CSS class applied to the outer container. |
213
- | `resizerClassName` | `string` | No | — | Custom CSS class applied to the drag handle. |
236
+ #### `generateUniqueId(): string`
214
237
 
215
- ---
238
+ Generates a unique pane ID string (e.g., `'pane-abc123xyz'`). Useful when creating new pane nodes programmatically.
216
239
 
217
- ## Tree Utilities
240
+ #### `removePane(tree: TreeNode | null, paneId: string): TreeNode | null`
218
241
 
219
- Import these serializable tree utility functions from `react-zeugma/utils` for programmatically mutating or querying layout schemas.
242
+ Recursively scans the layout tree, removes the targeted pane node, and collapses redundant split boundaries.
220
243
 
221
- #### `removePane(tree: TreeNode | null, id: string): TreeNode | null`
244
+ #### `removeTab(tree: TreeNode | null, tabId: string): TreeNode | null`
222
245
 
223
- Recursively scans the layout tree, removes the targeted pane node, and collapses redundant split boundaries.
246
+ Removes a single tab from its parent pane. If the pane has no remaining tabs after removal, the pane itself is collapsed out of the tree.
247
+
248
+ #### `addPane(tree: TreeNode | null, paneToAdd: string, metadata?: Record<string, unknown>): TreeNode`
249
+
250
+ Recursively matches the bottommost/rightmost pane leaf in the tree, splits it, and inserts the target `paneToAdd`. Optionally sets initial metadata for the new pane's tab.
224
251
 
225
- #### `addPane(tree: TreeNode | null, paneToAdd: string): TreeNode`
252
+ #### `addTab(tree: TreeNode | null, targetPaneId: string, tabId: string, metadata?: Record<string, unknown>): TreeNode | null`
226
253
 
227
- Recursively matches the bottommost/rightmost pane leaf in the tree, splits it, and inserts the target `paneToAdd`.
254
+ Appends a tab directly into a specific target pane node by its ID. Sets the new tab as the active tab. Does nothing if `targetPaneId` is not found.
228
255
 
229
256
  #### `splitPane(tree, targetId, direction, splitType, paneToAdd)`
230
257
 
231
- Splits the targeted `targetId` pane inside the tree with `direction` (_row_ / _column_) and type (_left_, _right_, _top_, _bottom_) to insert `paneToAdd`.
258
+ Splits the targeted `targetId` pane inside the tree with `direction` (_row_ / _column_) and type (_left_, _right_, _top_, _bottom_) to insert `paneToAdd`. `paneToAdd` may be a tab ID string or a full `PaneNode` object.
259
+
260
+ #### `updateSplitPercentage(tree: TreeNode | null, target: SplitNode, newPercentage: number): TreeNode | null`
261
+
262
+ Finds the target `SplitNode` by reference in the tree and updates its `splitPercentage`.
263
+
264
+ #### `selectTab(tree: TreeNode | null, paneId: string, tabId: string): TreeNode | null`
265
+
266
+ Activates the specified `tabId` within the `paneId` pane node. Returns the tree unchanged if the tab is already active or the pane is not found.
267
+
268
+ #### `mergeTab(tree: TreeNode | null, draggedTabId: string, targetPaneId: string): TreeNode | null`
269
+
270
+ Moves `draggedTabId` from its current pane into `targetPaneId`, preserving tab metadata. Collapses the source pane if it becomes empty. Sets the moved tab as the active tab in the target pane.
271
+
272
+ #### `moveTab(tree: TreeNode | null, draggedTabId: string, targetTabId: string, position?: 'before' | 'after'): TreeNode | null`
273
+
274
+ Reorders `draggedTabId` adjacent to `targetTabId` within the same pane (or moves it cross-pane). `position` defaults to `'before'`.
275
+
276
+ #### `updateTabMetadata(tree: TreeNode | null, tabId: string, updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined): TreeNode | null`
277
+
278
+ Updates the metadata of a specific tab using an updater function. Returning `undefined` from the updater removes the entry.
279
+
280
+ #### `updatePaneLock(tree: TreeNode | null, paneId: string, locked: boolean): TreeNode | null`
281
+
282
+ Sets the `locked` flag on the specified pane node. When `locked` is `false`, the field is removed from the node entirely to keep the tree clean.
232
283
 
233
284
  #### `findPaneById(tree: TreeNode | null, paneId: string): PaneNode | null`
234
285
 
@@ -242,6 +293,10 @@ Recursively searches the layout tree and returns the `PaneNode` containing the s
242
293
 
243
294
  Searches the layout tree for the given `tabId` and returns computed details (parent `paneId`, `isActive`, `index`, and custom `metadata`).
244
295
 
296
+ #### `computeLayout(node: TreeNode | null, left?, top?, width?, height?): { panes: ComputedPane[]; splitters: ComputedSplitter[] }`
297
+
298
+ Recursively computes the absolute position and dimensions (as percentages relative to the container) for every pane and splitter in the tree. Useful for building custom render layers or analytics on top of the layout engine.
299
+
245
300
  #### `calculateTabDropIndex(tabs: string[], activeType: string | null, overTabId: string | null, overTabPosition: 'before' | 'after' | null): number`
246
301
 
247
302
  Calculates the target insertion index for a dragged tab within a list of tabs. Returns `-1` if the drop target is not in the list.
@@ -261,17 +316,38 @@ Use custom CSS or styling rules to style resizers, dragging states, drop preview
261
316
  'bg-transparent hover:bg-indigo-500/50 active:bg-indigo-500 transition-colors duration-150',
262
317
  // split previews
263
318
  dropPreview: 'bg-indigo-500/10 border-2 border-dashed border-indigo-500/50 backdrop-blur-xs',
319
+ // tab separator line
320
+ tabSeparator: 'w-px h-4 bg-zinc-700',
264
321
  }}
265
322
  >
266
323
  <PaneTree />
267
324
  </Zeugma>
268
325
  ```
269
326
 
327
+ ### `ZeugmaClassNames` reference
328
+
329
+ | Key | Applied to |
330
+ | ------------------------ | --------------------------------------------------------------------------------- |
331
+ | `dashboard` | Root dashboard container. |
332
+ | `dashboardDismissActive` | Root container when a drag-out dismiss is active. |
333
+ | `dashboardLocked` | Root container when the dashboard is globally locked. |
334
+ | `pane` | Outer wrapper `<div>` of each `<Pane>`. |
335
+ | `paneLocked` | Pane container when locked. |
336
+ | `dropPreview` | Drop zone preview box when hovering over a split-edge drop zone. |
337
+ | `dragOverlay` | Cursor-following drag preview portal wrapper. |
338
+ | `resizer` | Drag-to-resize split bar handles. |
339
+ | `dismissPreview` | Background dismiss zone indicator during a drag-out dismiss gesture. |
340
+ | `lockedPreview` | Drop zone indicator when hovering over a locked pane. |
341
+ | `tabDropPreview` | Placeholder line element rendered at the target insertion point during tab drags. |
342
+ | `tabSeparator` | Separator line rendered between non-active adjacent tabs in `<Tabs>`. |
343
+
270
344
  ---
271
345
 
272
346
  ## Types Reference
273
347
 
274
- Full TypeScript type definitions utilized in the dashboard layout configuration.
348
+ Full TypeScript type definitions exported from `react-zeugma`.
349
+
350
+ ### Layout Tree
275
351
 
276
352
  ```ts
277
353
  export type SplitDirection = 'row' | 'column'
@@ -281,7 +357,7 @@ export interface SplitNode {
281
357
  direction: SplitDirection
282
358
  first: TreeNode
283
359
  second: TreeNode
284
- splitPercentage: number
360
+ splitPercentage: number // 0–100
285
361
  }
286
362
 
287
363
  export interface PaneNode {
@@ -304,6 +380,164 @@ export interface TabDetails {
304
380
  }
305
381
  ```
306
382
 
383
+ ### Component Props
384
+
385
+ ````ts
386
+ export interface PaneProps {
387
+ id: string
388
+ children: (props: PaneRenderProps) => React.ReactNode
389
+ style?: React.CSSProperties
390
+ locked?: boolean
391
+ }
392
+
393
+ export interface DragHandleProps {
394
+ children?: React.ReactNode
395
+ className?: string
396
+ style?: React.CSSProperties
397
+ }
398
+
399
+ export interface TabsProps {
400
+ tabs: string[]
401
+ activeTabId: string
402
+ locked?: boolean
403
+ tabsMetadata?: Record<string, Record<string, unknown>>
404
+ selectTab: (id: string) => void
405
+ removeTab: (id: string) => void
406
+ renderTab: (props: {
407
+ tabId: string
408
+ activeTabId: string
409
+ isDragging: boolean
410
+ isOver: boolean
411
+ metadata?: Record<string, unknown>
412
+ selectTab: (id: string) => void
413
+ removeTab: (id: string) => void
414
+ }) => React.ReactNode
415
+ classNames?: {
416
+ container?: string
417
+ tab?: string | ((tabId: string) => string)
418
+ }
419
+ styles?: {
420
+ container?: React.CSSProperties
421
+ tab?: React.CSSProperties | ((tabId: string) => React.CSSProperties)
422
+ }
423
+ }
424
+
425
+ ### Render Props
426
+
427
+ ```ts
428
+ export interface PaneRenderProps {
429
+ isDragging: boolean
430
+ isFullscreen: boolean
431
+ toggleFullscreen: () => void
432
+ remove: () => void
433
+ metadata: Record<string, unknown> | undefined
434
+ updateMetadata: (
435
+ updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined,
436
+ ) => void
437
+ locked: boolean
438
+ tabs: string[]
439
+ activeTabId: string
440
+ selectTab: (tabId: string) => void
441
+ removeTab: (tabId: string) => void
442
+ tabsMetadata: Record<string, Record<string, unknown>> | undefined
443
+ updateTabMetadata: (
444
+ tabId: string,
445
+ updater: (current: Record<string, unknown> | undefined) => Record<string, unknown> | undefined,
446
+ ) => void
447
+ renderActiveTab: () => ReactNode
448
+ }
449
+
450
+ export interface TabContextValue {
451
+ tabId: string
452
+ isActive: boolean
453
+ isDragging: boolean
454
+ isOver: boolean
455
+ metadata?: Record<string, unknown>
456
+ locked: boolean
457
+ selectTab: () => void
458
+ removeTab: () => void
459
+ }
460
+ ````
461
+
462
+ ### Controller & Context
463
+
464
+ ```ts
465
+ export interface UseZeugmaOptions { /* see useZeugma() section above */ }
466
+
467
+ export interface ZeugmaController {
468
+ // State
469
+ layout: TreeNode | null
470
+ setLayout: Dispatch<SetStateAction<TreeNode | null>>
471
+ fullscreenPaneId: string | null
472
+ setFullscreenPaneId: (paneId: string | null) => void
473
+ locked: boolean
474
+ setLocked: Dispatch<SetStateAction<boolean>>
475
+
476
+ // DnD state
477
+ activeId: string | null
478
+ activeType: 'pane' | 'tab' | null
479
+ dismissIntentId: string | null
480
+
481
+ // Config
482
+ dragActivationDistance: number
483
+ snapThreshold: number
484
+ minSplitPercentage: number
485
+ maxSplitPercentage: number
486
+ enableDragToDismiss: boolean
487
+ dismissThreshold: number
488
+
489
+ // Public actions
490
+ removePane: (paneId: string) => void
491
+ addPane: (paneId: string, metadata?: Record<string, unknown>) => void
492
+ addTab: (paneId: string, tabId: string, metadata?: Record<string, unknown>) => void
493
+ updateTabMetadata: (tabId: string, updater: ...) => void
494
+ updatePaneLock: (paneId: string, locked: boolean) => void
495
+ selectTab: (paneId: string, tabId: string) => void
496
+ mergeTab: (draggedTabId: string, targetPaneId: string) => void
497
+ removeTab: (tabId: string) => void
498
+ splitPane: (targetId: string, direction: SplitDirection, splitType: 'left' | 'right' | 'top' | 'bottom', paneToAdd: string) => void
499
+ updateSplitPercentage: (currentNode: SplitNode, percentage: number) => void
500
+ moveTab: (draggedTabId: string, targetTabId: string, position?: 'before' | 'after') => void
501
+
502
+ // Public queries
503
+ findPaneById: (paneId: string) => PaneNode | null
504
+ findPaneContainingTab: (tabId: string) => PaneNode | null
505
+ findTabById: (tabId: string) => TabDetails | null
506
+ }
507
+
508
+ // ZeugmaContextValue is the combined state + actions interface
509
+ // exposed by useZeugmaContext(). It includes all of ZeugmaController
510
+ // plus the renderPane and classNames configuration.
511
+ export interface ZeugmaContextValue extends ZeugmaStateValue, ZeugmaActionsValue {}
512
+ ```
513
+
514
+ ### Computed Layout Types (from `react-zeugma/utils`)
515
+
516
+ ```ts
517
+ export interface ComputedPane {
518
+ paneId: string
519
+ left: number // percentage
520
+ top: number // percentage
521
+ width: number // percentage
522
+ height: number // percentage
523
+ node: PaneNode
524
+ }
525
+
526
+ export interface ComputedSplitter {
527
+ id: string
528
+ currentNode: SplitNode
529
+ direction: SplitDirection
530
+ left: number
531
+ top: number
532
+ width: number
533
+ height: number
534
+ parentLeft: number
535
+ parentTop: number
536
+ parentWidth: number
537
+ parentHeight: number
538
+ }
539
+ ```
540
+
307
541
  ---
308
542
 
309
543
  ## SKILL.md
@@ -481,24 +715,6 @@ Defines the interactive drag region inside a `<Pane>`. **Must be placed inside a
481
715
  - `className?: string`
482
716
  - `style?: React.CSSProperties`
483
717
 
484
- ---
485
-
486
- ### `<ResizableContainer>`
487
-
488
- A vertical-resize container wrapper that wraps any node (typically `<PaneTree />` or a dashboard component) and allows the user to resize its height by dragging a handle at the bottom edge. Includes smooth scroll parent propagation and drag-to-scroll infinite scrolling behavior.
489
-
490
- #### Props
491
-
492
- - `height?: number` — Controlled height in pixels (or initial height if uncontrolled). Defaults to `400`.
493
- - `onHeightChange?: (height: number) => void` — Callback function triggered during or after dragging to resize.
494
- - `minHeight?: number` — Minimum allowed height in pixels (defaults to `100`).
495
- - `maxHeight?: number` — Maximum allowed height in pixels (defaults to `Infinity`).
496
- - `persist?: boolean` — Whether to persist height changes in localStorage.
497
- - `localStorageKey?: string` — Custom localStorage key name (defaults to `'default-pane'`).
498
- - `resizerHeight?: number` — Height of the resizer drag handle in pixels (defaults to `6`).
499
- - `className?: string` — Custom CSS class applied to the outer container.
500
- - `resizerClassName?: string` — Custom CSS class applied to the drag handle.
501
-
502
718
  ## 3. Programmatic State Utilities
503
719
 
504
720
  Import these helpers from `react-zeugma/utils` to manipulate or query the tree layout programmatically in your state handlers:
@@ -591,6 +807,7 @@ interface ZeugmaClassNames {
591
807
  dismissPreview?: string // Applied to the background dismiss zone indicator during a drag-out dismiss gesture
592
808
  lockedPreview?: string // Applied to drop zone indicator when hovering over a locked pane
593
809
  tabDropPreview?: string // Applied to the drop placeholder line element during tab drags
810
+ tabSeparator?: string // Applied to the separator line between non-active adjacent tabs
594
811
  }
595
812
  ```
596
813