@blorkfield/blork-tabs 0.3.0 → 0.4.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
@@ -8,6 +8,7 @@ A framework-agnostic tab/panel management system with snapping and docking capab
8
8
  - **Anchor Docking** - Dock panels to predefined screen positions
9
9
  - **Drag Modes** - Drag entire groups or detach individual panels
10
10
  - **Collapse/Expand** - Panels can be collapsed with automatic repositioning
11
+ - **Pin** - Pin panels to prevent dragging and opt out of auto-hide
11
12
  - **Auto-Hide** - Panels can hide after inactivity and show on interaction
12
13
  - **Event System** - Subscribe to drag, snap, and collapse events
13
14
  - **Fully Typed** - Complete TypeScript support
@@ -54,6 +55,7 @@ manager.registerPanel('my-panel', document.getElementById('my-panel'), {
54
55
  });
55
56
 
56
57
  // Position panels and create snap chains
58
+ // positionPanelsFromRight takes IDs right-to-left; createSnapChain takes them left-to-right
57
59
  manager.positionPanelsFromRight(['tools', 'settings']);
58
60
  manager.createSnapChain(['settings', 'tools']);
59
61
 
@@ -99,6 +101,40 @@ const manager = new TabManager({
99
101
  });
100
102
  ```
101
103
 
104
+ ## Panel Configuration
105
+
106
+ Each panel accepts the following options via `addPanel(config)`:
107
+
108
+ ```typescript
109
+ manager.addPanel({
110
+ id: 'my-panel', // Required. Unique identifier.
111
+ title: 'My Panel', // Header text.
112
+ width: 300, // Panel width in px (default: 300).
113
+ initialPosition: { x: 100, y: 100 }, // Starting position. Auto-placed if omitted.
114
+ content: '<div>...</div>', // HTML string or HTMLElement for the content area.
115
+
116
+ // Collapse
117
+ startCollapsed: true, // Whether the panel starts collapsed (default: true).
118
+ collapsible: true, // Show collapse button (default: true).
119
+
120
+ // Pin
121
+ pinnable: false, // Show pin button in header (default: false).
122
+ startPinned: false, // Initial pin state (default: false).
123
+
124
+ // Drag
125
+ draggable: true, // Allow dragging (default: true).
126
+ detachable: true, // Show detach grip for single-panel drag (default: true).
127
+
128
+ // Auto-hide overrides (see Auto-Hide section)
129
+ startHidden: false, // Override global startHidden for this panel.
130
+ autoHideDelay: undefined, // Override global autoHideDelay (0 = disable auto-hide).
131
+
132
+ // Z-index
133
+ zIndex: 1000, // Default z-index (default: 1000).
134
+ dragZIndex: 1002, // Z-index while dragging (default: 1002).
135
+ });
136
+ ```
137
+
102
138
  ## API Reference
103
139
 
104
140
  ### TabManager
@@ -115,12 +151,23 @@ const manager = new TabManager({
115
151
  - `getSnapChain(panelId)` - Get all panels in same chain
116
152
  - `snap(leftPanelId, rightPanelId)` - Manually snap two panels
117
153
  - `detach(panelId)` - Detach panel from chain
118
- - `createSnapChain(panelIds)` - Create chain from panel IDs
154
+ - `createSnapChain(panelIds)` - Create chain from panel IDs (left-to-right order)
119
155
  - `updatePositions()` - Recalculate snapped positions
120
156
 
121
157
  #### Positioning
122
- - `positionPanelsFromRight(panelIds, gap?)` - Position from right edge
123
- - `positionPanelsFromLeft(panelIds, gap?)` - Position from left edge
158
+
159
+ `positionPanelsFromRight` and `positionPanelsFromLeft` take panel IDs in **opposite orders**:
160
+
161
+ - `positionPanelsFromRight(panelIds, gap?)` — first ID is placed at the **right edge**, rest go leftward. Pass IDs **right-to-left**.
162
+ - `positionPanelsFromLeft(panelIds, gap?)` — first ID is placed at the **left edge**, rest go rightward. Pass IDs **left-to-right**.
163
+
164
+ Because `createSnapChain` uses left-to-right order, you'll typically reverse the array between the two calls:
165
+
166
+ ```typescript
167
+ // Visual order left-to-right: properties, tools, settings
168
+ manager.positionPanelsFromRight(['settings', 'tools', 'properties']); // right-to-left
169
+ manager.createSnapChain(['properties', 'tools', 'settings']); // left-to-right
170
+ ```
124
171
 
125
172
  #### Anchors
126
173
  - `addAnchor(config)` - Add custom anchor
@@ -149,10 +196,54 @@ const manager = new TabManager({
149
196
  | `snap:panel` | Panels snapped together |
150
197
  | `snap:anchor` | Panels snapped to anchor |
151
198
  | `panel:detached` | Panel detached from chain |
199
+ | `panel:pin` | Panel pinned/unpinned |
152
200
  | `panel:collapse` | Panel collapsed/expanded |
153
201
  | `panel:show` | Panel became visible (auto-hide) |
154
202
  | `panel:hide` | Panel became hidden (auto-hide) |
155
203
 
204
+ ## Drag Modes
205
+
206
+ Each panel header has two drag zones:
207
+
208
+ - **Detach grip** (small handle on the left of the header) — drags only that panel. It is detached from its snap chain and moved independently.
209
+ - **Title / header area** — drags the entire connected snap chain as a group.
210
+
211
+ Set `detachable: false` to hide the grip and make the header always drag the group.
212
+
213
+ ## Pinning
214
+
215
+ Pinning locks a panel in place and exempts it from auto-hide. Enable the pin button with `pinnable: true`:
216
+
217
+ ```typescript
218
+ manager.addPanel({
219
+ id: 'hud',
220
+ title: 'HUD',
221
+ pinnable: true,
222
+ startPinned: true, // start already pinned
223
+ autoHideDelay: 5000, // pin will override this
224
+ });
225
+ ```
226
+
227
+ ### What pinning does
228
+
229
+ - **Prevents dragging** — a pinned panel cannot be moved, even when dragging its header.
230
+ - **Immune to auto-hide** — a pinned panel is always visible regardless of the `autoHideDelay`. Pinning cancels any pending hide timer and shows the panel if it was hidden. Unpinning restarts the timer.
231
+ - **Splits snap chains on drag** — if a pinned panel sits in the middle of a chain and you drag a neighbour, the chain severs at the pin boundary. Only the panels on the same side as the grabbed panel move; the pinned panel and everything beyond it stay put.
232
+
233
+ ```typescript
234
+ // Chain: A — B — [P pinned] — C — D
235
+ // Grabbing B moves [A, B] and severs the B↔P bond.
236
+ // Grabbing C moves [C, D] and severs the P↔C bond.
237
+ ```
238
+
239
+ ### Events
240
+
241
+ ```typescript
242
+ manager.on('panel:pin', ({ panel, isPinned }) => {
243
+ console.log(`${panel.id} is now ${isPinned ? 'pinned' : 'unpinned'}`);
244
+ });
245
+ ```
246
+
156
247
  ## Multi-Section Panel Content
157
248
 
158
249
  When creating panels with multiple sections (like a command menu with categories), put all sections within a **single `content` string**. Do not use multiple panels or multiple content wrappers—this breaks scrolling and causes content cutoff.
@@ -231,6 +322,10 @@ manager.addPanel({
231
322
  | `false` | `3000` | Starts visible, hides after 3s of inactivity |
232
323
  | `true` | `3000` | Starts hidden, shows on activity, hides after 3s of inactivity |
233
324
 
325
+ ### Pin interaction
326
+
327
+ Pinning a panel overrides auto-hide entirely for that panel — it stays visible regardless of inactivity. Unpinning re-enables the timer. Each panel is tracked independently; pinning one panel in a group has no effect on its neighbours.
328
+
234
329
  ### Events
235
330
 
236
331
  ```typescript
@@ -312,11 +407,13 @@ debug.clear();
312
407
 
313
408
  The debug panel has a special "focus mode" for reading logs:
314
409
 
315
- 1. **Hover for 5 seconds** → Panel enlarges to 75% of screen with doubled text size
410
+ 1. **Hover for configurable delay** → Panel enlarges to 75% of screen with doubled text size (default: 5 seconds, configurable via `hoverDelay`)
316
411
  2. **Mouse can move freely** → Panel stays enlarged, won't close on mouse leave
317
412
  3. **Click × or backdrop** → Returns to normal size
318
413
 
319
- The × button is only visible when enlarged.
414
+ The × button is only visible when enlarged. Set `hoverDelay: 0` to disable the enlarge feature entirely.
415
+
416
+ **Auto-hide interaction:** When hovering a debug panel that has auto-hide enabled, the auto-hide timer is paused so the panel won't disappear before the hover-to-enlarge triggers.
320
417
 
321
418
  ### Configuration Options
322
419
 
@@ -324,6 +421,7 @@ The × button is only visible when enlarged.
324
421
  |--------|------|---------|-------------|
325
422
  | `maxEntries` | `number` | `50` | Maximum log entries before oldest are removed |
326
423
  | `showTimestamps` | `boolean` | `false` | Show timestamps on each entry |
424
+ | `hoverDelay` | `number` | `5000` | Milliseconds to hover before enlarging (0 = disable) |
327
425
 
328
426
  Plus all standard `PanelConfig` options (`id`, `title`, `width`, `initialPosition`, `startCollapsed`, etc.)
329
427
 
@@ -334,6 +432,41 @@ Plus all standard `PanelConfig` options (`id`, `title`, `width`, `initialPositio
334
432
  debug.panel; // PanelState - for advanced manipulation
335
433
  ```
336
434
 
435
+ ### Embeddable Debug Log
436
+
437
+ You can embed a debug log inside any panel or container element using `createDebugLog()`:
438
+
439
+ ```typescript
440
+ const manager = new TabManager();
441
+
442
+ // Create a panel with custom content
443
+ const panel = manager.addPanel({
444
+ id: 'my-panel',
445
+ title: 'My Panel',
446
+ content: '<div id="my-content">Some content</div>',
447
+ });
448
+
449
+ // Add a debug log section to the panel
450
+ const logSection = document.createElement('div');
451
+ panel.contentWrapper.appendChild(logSection);
452
+
453
+ // Create the embedded debug log (shares hover-to-enlarge with standalone panels)
454
+ const embeddedLog = manager.createDebugLog(logSection, {
455
+ maxEntries: 20,
456
+ showTimestamps: true,
457
+ hoverDelay: 3000, // 3 second hover delay (0 to disable, default: 5000)
458
+ });
459
+
460
+ // Use it like a regular debug panel
461
+ embeddedLog.log('event', { data: 'value' });
462
+ embeddedLog.info('status', { connected: true });
463
+ embeddedLog.warn('warning', { message: 'Low memory' });
464
+ embeddedLog.error('error', { code: 500 });
465
+ embeddedLog.clear();
466
+ ```
467
+
468
+ The embedded log supports the same hover-to-enlarge behavior as the standalone debug panel.
469
+
337
470
  ## CSS Customization
338
471
 
339
472
  Override CSS variables to customize appearance: