@principal-ade/panel-layouts 0.1.3 → 0.1.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 +29 -29
- package/dist/index.css +1 -1
- package/dist/index.d.ts +18 -12
- package/dist/index.esm.js +12 -11
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
> Pre-built panel layout components and workspace management for the Panel Framework
|
|
4
4
|
|
|
5
|
-
This package provides ready-to-use panel layout components, workspace presets, and persistence hooks that work with `@principal-ade/panel-framework-core`. Built on top of `@
|
|
5
|
+
This package provides ready-to-use panel layout components, workspace presets, and persistence hooks that work with `@principal-ade/panel-framework-core`. Built on top of `@principal-ade/panels`, it adds workspace management, persistence, and pre-configured layouts from the electron-app.
|
|
6
6
|
|
|
7
7
|
## 🎯 Purpose
|
|
8
8
|
|
|
9
9
|
While `@principal-ade/panel-framework-core` provides the foundation for how panels *work* (registration, lifecycle, events), this package handles how panels *arrange on screen* (layouts, resizing, workspaces).
|
|
10
10
|
|
|
11
|
-
This package = **@
|
|
11
|
+
This package = **@principal-ade/panels** (layout primitives) + **persistence** + **workspace management** + **preset layouts**
|
|
12
12
|
|
|
13
13
|
## 📦 What This Package Provides
|
|
14
14
|
|
|
15
|
-
### Re-exported Components (from @
|
|
15
|
+
### Re-exported Components (from @principal-ade/panels)
|
|
16
16
|
|
|
17
|
-
We re-export the core layout components from `@
|
|
17
|
+
We re-export the core layout components from `@principal-ade/panels` for convenience:
|
|
18
18
|
|
|
19
19
|
- **`ConfigurablePanelLayout`** - Main layout component supporting 2 or 3 panel configurations
|
|
20
20
|
- **`TabGroup`** - Tab-based panel grouping
|
|
@@ -26,7 +26,7 @@ These components provide:
|
|
|
26
26
|
- Resizable panels with drag handles (powered by `react-resizable-panels`)
|
|
27
27
|
- Collapsible panels with animations
|
|
28
28
|
- Tab groups and tile arrangements
|
|
29
|
-
- Theme integration with `@
|
|
29
|
+
- Theme integration with `@principal-ade/industry-theme`
|
|
30
30
|
- 2-panel and 3-panel layout support
|
|
31
31
|
|
|
32
32
|
### Our Value-Add: Hooks (Extracted from electron-app)
|
|
@@ -90,10 +90,10 @@ Three-panel layout optimized for repository exploration:
|
|
|
90
90
|
|
|
91
91
|
## 📋 Type Definitions
|
|
92
92
|
|
|
93
|
-
### Re-exported from @
|
|
93
|
+
### Re-exported from @principal-ade/panels
|
|
94
94
|
|
|
95
95
|
```typescript
|
|
96
|
-
// Panel Layout Structure (from @
|
|
96
|
+
// Panel Layout Structure (from @principal-ade/panels)
|
|
97
97
|
interface PanelLayout {
|
|
98
98
|
left?: PanelSlot;
|
|
99
99
|
middle?: PanelSlot;
|
|
@@ -246,7 +246,7 @@ WorkspaceLayoutService.createCustomWorkspace({
|
|
|
246
246
|
```
|
|
247
247
|
@principal-ade/panel-layouts/
|
|
248
248
|
├── src/
|
|
249
|
-
│ ├── index.ts # Main entry - re-exports from @
|
|
249
|
+
│ ├── index.ts # Main entry - re-exports from @principal-ade/panels + our code
|
|
250
250
|
│ ├── layouts/
|
|
251
251
|
│ │ ├── FeedViewLayout.tsx # Pre-built feed view
|
|
252
252
|
│ │ ├── ProjectManagementLayout.tsx # Project management preset
|
|
@@ -271,7 +271,7 @@ WorkspaceLayoutService.createCustomWorkspace({
|
|
|
271
271
|
└── LICENSE
|
|
272
272
|
```
|
|
273
273
|
|
|
274
|
-
**Note:** We don't duplicate `ConfigurablePanelLayout`, `TabGroup`, etc. - we re-export them from `@
|
|
274
|
+
**Note:** We don't duplicate `ConfigurablePanelLayout`, `TabGroup`, etc. - we re-export them from `@principal-ade/panels`.
|
|
275
275
|
|
|
276
276
|
## 🔗 Dependencies
|
|
277
277
|
|
|
@@ -283,40 +283,40 @@ WorkspaceLayoutService.createCustomWorkspace({
|
|
|
283
283
|
"react-dom": ">=18.0.0"
|
|
284
284
|
},
|
|
285
285
|
"dependencies": {
|
|
286
|
-
"@
|
|
287
|
-
"@
|
|
286
|
+
"@principal-ade/panels": "^1.0.39",
|
|
287
|
+
"@principal-ade/industry-theme": "^0.1.2",
|
|
288
288
|
"react-resizable-panels": "^3.0.0"
|
|
289
289
|
}
|
|
290
290
|
}
|
|
291
291
|
```
|
|
292
292
|
|
|
293
293
|
**Architecture:**
|
|
294
|
-
- We depend on `@
|
|
295
|
-
- `@
|
|
296
|
-
- We re-export components from `@
|
|
294
|
+
- We depend on `@principal-ade/panels` for layout primitives (ConfigurablePanelLayout, TabGroup, etc.)
|
|
295
|
+
- `@principal-ade/panels` depends on `react-resizable-panels` for resizing functionality
|
|
296
|
+
- We re-export components from `@principal-ade/panels` for convenience
|
|
297
297
|
- We add our own persistence, workspace management, and preset layouts on top
|
|
298
298
|
|
|
299
299
|
## 🎨 Design Principles
|
|
300
300
|
|
|
301
301
|
### 1. Layered Architecture
|
|
302
302
|
- **@principal-ade/panel-framework-core:** Panel registration, lifecycle, events
|
|
303
|
-
- **@
|
|
303
|
+
- **@principal-ade/panels:** Layout primitives, resizing, collapsing, theming
|
|
304
304
|
- **@principal-ade/panel-layouts (this package):** Persistence, workspaces, presets
|
|
305
305
|
|
|
306
306
|
### 2. Reuse Over Rebuild
|
|
307
|
-
We build on top of proven components (`@
|
|
307
|
+
We build on top of proven components (`@principal-ade/panels`) rather than reinventing the wheel. This allows us to:
|
|
308
308
|
- Focus on high-value features (workspaces, persistence)
|
|
309
309
|
- Get resizing, animations, and theming for free
|
|
310
310
|
- Maintain compatibility with existing electron-app code
|
|
311
311
|
|
|
312
312
|
### 3. Optional Dependency
|
|
313
|
-
Users can use `@principal-ade/panel-framework-core` + `@
|
|
313
|
+
Users can use `@principal-ade/panel-framework-core` + `@principal-ade/panels` directly if they don't need workspace management.
|
|
314
314
|
|
|
315
315
|
### 4. Composable
|
|
316
316
|
All components are composable - users can:
|
|
317
317
|
- Use pre-built layouts as-is (`FeedViewLayout`)
|
|
318
318
|
- Use `ConfigurablePanelLayout` with custom configs
|
|
319
|
-
- Use `@
|
|
319
|
+
- Use `@principal-ade/panels` directly for full control
|
|
320
320
|
|
|
321
321
|
### 5. Persistence-Ready
|
|
322
322
|
Built-in support for saving/loading layout state to:
|
|
@@ -328,11 +328,11 @@ Built-in support for saving/loading layout state to:
|
|
|
328
328
|
|
|
329
329
|
### Phase 1: Package Setup and Re-exports
|
|
330
330
|
1. ✅ Set up package structure with TypeScript and Vite
|
|
331
|
-
2. ✅ Install `@
|
|
332
|
-
3. ✅ Create re-export index that exposes components from `@
|
|
331
|
+
2. ✅ Install `@principal-ade/panels` as a dependency
|
|
332
|
+
3. ✅ Create re-export index that exposes components from `@principal-ade/panels`
|
|
333
333
|
4. ✅ Configure build to output ESM/CJS with CSS
|
|
334
334
|
|
|
335
|
-
**What we're doing:** Setting up the foundation and making `@
|
|
335
|
+
**What we're doing:** Setting up the foundation and making `@principal-ade/panels` components available through our package.
|
|
336
336
|
|
|
337
337
|
### Phase 2: Extract Persistence Hook (High Priority)
|
|
338
338
|
1. Extract `usePanelPersistence` from electron-app
|
|
@@ -343,7 +343,7 @@ Built-in support for saving/loading layout state to:
|
|
|
343
343
|
**Source File:**
|
|
344
344
|
- `/Users/griever/Developer/electron-app/src/renderer/hooks/usePanelPersistence.ts`
|
|
345
345
|
|
|
346
|
-
**What we're doing:** Adding the state persistence layer that `@
|
|
346
|
+
**What we're doing:** Adding the state persistence layer that `@principal-ade/panels` doesn't provide.
|
|
347
347
|
|
|
348
348
|
### Phase 3: Extract Workspace Service
|
|
349
349
|
1. Extract `WorkspaceLayoutService` from electron-app
|
|
@@ -380,7 +380,7 @@ Built-in support for saving/loading layout state to:
|
|
|
380
380
|
Once published, the electron-app can replace its current implementation:
|
|
381
381
|
|
|
382
382
|
```diff
|
|
383
|
-
- import { ConfigurablePanelLayout } from '@
|
|
383
|
+
- import { ConfigurablePanelLayout } from '@principal-ade/panels';
|
|
384
384
|
- import { usePanelPersistence } from '../hooks/usePanelPersistence';
|
|
385
385
|
+ import { ConfigurablePanelLayout, usePanelPersistence } from '@principal-ade/panel-layouts';
|
|
386
386
|
```
|
|
@@ -395,8 +395,8 @@ Benefits:
|
|
|
395
395
|
|
|
396
396
|
- **@principal-ade/panel-framework-core** - Core panel system (published)
|
|
397
397
|
- **@principal-ade/panel-layouts** - This package (to be implemented)
|
|
398
|
-
- **@
|
|
399
|
-
- **@
|
|
398
|
+
- **@principal-ade/panels** - Current layout implementation in electron-app
|
|
399
|
+
- **@principal-ade/industry-theme** - Theming system used by layouts
|
|
400
400
|
|
|
401
401
|
## 🤝 Contributing
|
|
402
402
|
|
|
@@ -417,12 +417,12 @@ MIT
|
|
|
417
417
|
|
|
418
418
|
**Status:** 🚧 In Active Development
|
|
419
419
|
|
|
420
|
-
This package is being actively developed with a focus on reusing `@
|
|
420
|
+
This package is being actively developed with a focus on reusing `@principal-ade/panels` and adding workspace/persistence features.
|
|
421
421
|
|
|
422
422
|
### Implementation Progress:
|
|
423
423
|
- ✅ README specification complete
|
|
424
424
|
- ✅ Package setup and build configuration (Vite + TypeScript)
|
|
425
|
-
- ✅ Re-exports from @
|
|
425
|
+
- ✅ Re-exports from @principal-ade/panels (25+ components)
|
|
426
426
|
- ✅ `usePanelPersistence` hook extracted and adapted
|
|
427
427
|
- ✅ `LocalStoragePersistenceAdapter` for web apps
|
|
428
428
|
- ✅ Storybook with interactive component stories
|
|
@@ -434,7 +434,7 @@ This package is being actively developed with a focus on reusing `@a24z/panels`
|
|
|
434
434
|
- ⏳ Publish to npm
|
|
435
435
|
|
|
436
436
|
### Architecture Decision:
|
|
437
|
-
We're **reusing** `@
|
|
437
|
+
We're **reusing** `@principal-ade/panels@1.0.39` as a dependency rather than rebuilding layout components. This allows us to:
|
|
438
438
|
- Focus on high-value features (persistence, workspaces, presets)
|
|
439
439
|
- Leverage proven, tested layout components
|
|
440
440
|
- Maintain compatibility with existing electron-app code
|
|
@@ -455,7 +455,7 @@ npm run build # Build ESM + CJS + types ✅
|
|
|
455
455
|
```
|
|
456
456
|
|
|
457
457
|
### Package Contents:
|
|
458
|
-
- **Re-exported:** All components from @
|
|
458
|
+
- **Re-exported:** All components from @principal-ade/panels
|
|
459
459
|
- **New:** `usePanelPersistence` hook with localStorage adapter
|
|
460
460
|
- **New:** Comprehensive TypeScript types
|
|
461
461
|
- **New:** Interactive Storybook documentation
|
package/dist/index.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.animated-resizable-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.hybrid-panel{overflow:auto;height:100%;background-color:var(--panel-background)}.resize-handle{width:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:col-resize;position:relative;transition:background-color .2s,opacity .3s,width .3s}.resize-handle.collapsed{opacity:0;pointer-events:none}.resize-handle:hover{background-color:var(--panel-handle-hover)}.resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{height:100%;width:100%;display:flex;align-items:center;justify-content:center;position:relative}.collapse-toggle{position:absolute;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.animated-vertical-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.vertical-panel{overflow:auto;width:100%;background-color:var(--panel-background)}.panel-content-wrapper{width:100%;height:100%;overflow:auto}.vertical-resize-handle{height:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:row-resize;position:relative;transition:background-color .2s,opacity .3s,height .3s}.vertical-resize-handle.collapsed{opacity:0;pointer-events:none}.vertical-resize-handle:hover{background-color:var(--panel-handle-hover)}.vertical-resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{width:100%;height:100%;display:flex;align-items:center;justify-content:center;gap:8px;position:relative}.collapse-toggle{background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.collapse-toggle:hover:not(:disabled){background-color:var(--panel-button-hover);box-shadow:0 2px 6px #00000026}.collapse-toggle:active:not(:disabled){opacity:.8}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important}.panel-content-wrapper{flex:1;overflow:auto;will-change:opacity;box-sizing:border-box}.resize-handle.collapsed{width:0!important}.tab-group{display:flex;height:100%;width:100%;overflow:hidden}.tab-group.tab-position-top,.tab-group.tab-position-bottom{flex-direction:column}.tab-group.tab-position-left,.tab-group.tab-position-right{flex-direction:row}.tab-list{display:flex;background:var(--tab-list-bg, #f5f5f5);border-bottom:1px solid var(--tab-border, #ddd);gap:0;padding:0;flex-shrink:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.tab-list::-webkit-scrollbar{display:none}.tab-position-top .tab-list,.tab-position-bottom .tab-list{width:100%}.tab-list.centered{justify-content:flex-start}@media (min-width: 0){.tab-list.centered{justify-content:center}.tab-list.centered:has(.tab-button:nth-child(n)){justify-content:flex-start}}.tab-position-bottom .tab-list{border-bottom:none;border-top:none}.tab-position-left .tab-list,.tab-position-right .tab-list{flex-direction:column;border-bottom:none;border-right:none;width:auto;min-width:120px}.tab-position-top .tab-button,.tab-position-bottom .tab-button{flex:1 1 0;min-width:40px;max-width:100%}.tab-button{background:var(--tab-bg, #fff);border:1px solid var(--tab-border, #ddd);border-radius:0;padding:8px 16px;cursor:pointer;font-size:14px;font-weight:500;color:var(--tab-text, #333);transition:all .2s ease;white-space:nowrap;display:flex;align-items:center;justify-content:center;gap:6px;height:40px;line-height:1;box-sizing:border-box;container-type:inline-size}.tab-position-top .tab-button{border-bottom:none;border-top:none;border-left:none}.tab-position-top .tab-button:last-child{border-right:none}.tab-position-bottom .tab-button{border-top:none;border-bottom:none;border-left:none}.tab-position-bottom .tab-button:last-child{border-right:none}.tab-position-left .tab-button{border-right:none;border-bottom:none}.tab-position-left .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-position-right .tab-button{border-left:none;border-bottom:none}.tab-position-right .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-icon{display:inline-flex;align-items:center;justify-content:center}.tab-label{display:none}@container (min-width: 250px){.tab-label{display:inline}.tab-icon{display:none}}.tab-button:hover{background:var(--tab-bg-hover, #f9f9f9)}.tab-button.active{background:var(--tab-bg-active, #007bff);color:var(--tab-text-active, #fff);border-color:var(--tab-border-active, #007bff)}.tab-button:focus-visible{outline:2px solid var(--tab-focus, #007bff);outline-offset:2px}.tab-content{flex:1;overflow:auto;background:var(--tab-content-bg, #fff)}.tab-group-empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--tab-empty-text, #999);font-style:italic}.three-panel-layout{height:100%;width:100%;display:flex;flex-direction:column;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item{display:flex;flex-direction:column;overflow:hidden;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item.collapsible-panel{will-change:flex}.three-panel-item.collapsible-panel.animating{pointer-events:none}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important;max-width:0!important;width:0!important;overflow:hidden!important;visibility:hidden}.three-panel-item.middle-panel{flex:1;min-width:200px}.panel-content-wrapper{flex:1;overflow-x:hidden;overflow-y:auto;will-change:opacity;box-sizing:border-box}.resize-handle{position:relative;display:flex;align-items:center;justify-content:center;width:1px!important;cursor:col-resize;background:var(--panel-border);overflow:visible!important}.resize-handle:before{content:"";position:absolute;top:0;right:-10px;bottom:0;left:-10px;background:transparent}.resize-handle:after{content:"";position:absolute;top:0;right:-10px;bottom:0;left:-10px;background:var(--panel-handle);opacity:0;transition:opacity .2s ease;z-index:-1}.resize-handle:hover:after{opacity:1}.resize-handle:hover{background:var(--panel-handle-hover)}.resize-handle:active:after{opacity:1;background:var(--panel-handle-active)}.resize-handle:active{background:var(--panel-handle-active)}.resize-handle.collapsed{width:0!important;visibility:hidden}.resize-handle.left-handle.collapsed{margin-right:-1px}.resize-handle.right-handle.collapsed{margin-left:-1px}.handle-bar{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;z-index:2}.collapse-toggle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:20px;height:40px;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);border-radius:4px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;color:var(--panel-button-icon);transition:all .2s ease;z-index:10;padding:0;line-height:1}.collapse-toggle:hover{background:var(--panel-button-hover)}.collapse-toggle:active{opacity:.8}.collapse-toggle:disabled{opacity:.5;cursor:not-allowed}@media (max-width: 768px){.resize-handle:before{left:-8px;right:-8px}.resize-handle:after{left:-8px;right:-8px}.collapse-toggle{width:24px;height:48px;font-size:14px}}@keyframes wiggle{0%{transform:rotate(0)}25%{transform:rotate(1deg)}50%{transform:rotate(0)}75%{transform:rotate(-1deg)}to{transform:rotate(0)}}@keyframes scaleIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}@keyframes pulse{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 0 8px #3b82f600}}.editable-panel-layout{position:relative;width:100%;height:100%}.editable-panel-layout.edit-mode-active{background:#0000000d}[data-slot][data-edit-mode=true]{transform:scale(.95);transform-origin:center center;cursor:grab;will-change:transform;transition:transform .3s cubic-bezier(.4,0,.2,1)}[data-slot][data-edit-mode=true]:active{cursor:grabbing}[data-slot][data-dragging=true]{cursor:grabbing!important;transform:scale(.95)!important;transition:none!important}.edit-mode-active [data-slot][data-edit-mode=true]{transform-origin:center center}.slot-with-overlay{position:relative;width:100%;height:100%}.slot-with-overlay.dragging{opacity:0;pointer-events:none}.slot-edit-overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;border:none;pointer-events:auto;cursor:grab;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center}.slot-edit-overlay:hover{background:#3b82f608}.slot-edit-overlay:active{cursor:grabbing}.drag-indicator,.slot-position-label{display:none}.edit-mode-toggle{position:absolute;top:16px;right:16px;z-index:1000;padding:8px 16px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;box-shadow:0 2px 4px #0000001a}.edit-mode-toggle:hover{background:#f9fafb;box-shadow:0 4px 6px #0000001a}.edit-mode-toggle.active{background:#3b82f6;color:#fff;border-color:#2563eb}.edit-mode-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000004d;z-index:998;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.edit-mode-configurator{position:relative;z-index:999;padding:20px;animation:scaleIn .3s ease}.panel-slot{position:relative;min-height:120px;border:2px dashed transparent;border-radius:12px;transition:all .3s ease;padding:12px}.panel-slot.edit-mode{border-color:#d1d5db;background:#ffffff80}.panel-slot.drag-over{border-color:#3b82f6;background:#3b82f61a;box-shadow:0 0 0 4px #3b82f61a}.panel-slot.empty{display:flex;align-items:center;justify-content:center;color:#9ca3af;font-size:14px}.draggable-panel{position:relative;padding:12px 16px;margin:8px 0;border-radius:8px;background:#fff;border:1px solid #e5e7eb;cursor:grab;transition:all .2s ease;-webkit-user-select:none;user-select:none}.draggable-panel:hover{border-color:#3b82f6;box-shadow:0 2px 8px #0000001a}.draggable-panel.dragging{opacity:.5;cursor:grabbing}.draggable-panel.edit-mode{animation:wiggle .4s ease-in-out infinite;transform-origin:center}.draggable-panel.edit-mode:nth-child(odd){animation-delay:.1s}.draggable-panel.edit-mode:nth-child(2n){animation-delay:.2s}.remove-button{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#ef4444;color:#fff;border:2px solid white;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;font-weight:700;opacity:0;transform:scale(0);transition:all .2s ease;z-index:10;box-shadow:0 2px 4px #0003}.draggable-panel.edit-mode .remove-button{opacity:1;transform:scale(1);animation:pulse 2s infinite}.remove-button:hover{background:#dc2626;transform:scale(1.1)}.panel-label{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500;color:#374151}.panel-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.drag-handle{display:none;width:24px;height:24px;opacity:.4;cursor:grab}.edit-mode .drag-handle{display:flex;align-items:center;justify-content:center}.drag-overlay{padding:12px 16px;border-radius:8px;background:#fff;border:2px solid #3b82f6;box-shadow:0 8px 16px #0003;cursor:grabbing;opacity:.9}.panel-group{border:2px solid #e5e7eb;border-radius:12px;padding:8px;background:#fff}.panel-group.edit-mode{animation:wiggle .5s ease-in-out infinite;border-color:#3b82f6}.panel-group-header{display:flex;justify-content:space-between;align-items:center;padding:8px;border-bottom:1px solid #e5e7eb;margin-bottom:8px}.panel-group-title{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase}.available-panels{background:#fff;border-radius:12px;padding:16px;box-shadow:0 4px 6px #0000001a}.available-panels-title{font-size:16px;font-weight:600;color:#374151;margin-bottom:12px}.available-panels-list{display:flex;flex-direction:column;gap:8px}.slot-label{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center;gap:8px}.slot-indicator{width:8px;height:8px;border-radius:50%;background:#d1d5db}.slot-indicator.active{background:#3b82f6}.action-buttons{position:fixed;bottom:24px;left:50%;transform:translate(-50%);z-index:1001;display:flex;gap:12px;padding:12px 24px;background:#fff;border-radius:12px;box-shadow:0 8px 16px #00000026;animation:scaleIn .3s ease}.action-button{padding:10px 20px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease}.action-button:hover{background:#f9fafb}.action-button.primary{background:#3b82f6;color:#fff;border-color:#2563eb}.action-button.primary:hover{background:#2563eb}@media (max-width: 768px){.edit-mode-toggle{top:8px;right:8px;padding:6px 12px;font-size:12px}.action-buttons{bottom:16px;padding:10px 16px}.action-button{padding:8px 16px;font-size:13px}}@media (prefers-color-scheme: dark){.edit-mode-toggle{background:#1f2937;color:#f9fafb;border-color:#374151}.edit-mode-toggle:hover{background:#374151}.draggable-panel{background:#1f2937;color:#f9fafb;border-color:#374151}.available-panels{background:#1f2937}.panel-label{color:#f9fafb}.action-buttons{background:#1f2937}.action-button{background:#1f2937;color:#f9fafb;border-color:#374151}.action-button:hover{background:#374151}}.panel-configurator{display:flex;flex-direction:column;gap:2rem;padding:1.5rem;background:var(--configurator-bg);border-radius:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.configurator-section{display:flex;flex-direction:column;gap:1rem}.section-title{margin:0;font-size:.875rem;font-weight:600;color:var(--configurator-title);text-transform:uppercase;letter-spacing:.05em;text-align:center}.slots-container{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;width:75%;margin:0 auto;align-items:start}.slot{position:relative;aspect-ratio:1 / 1.3;width:100%;padding:2.5rem 1rem 1rem;background:var(--slot-bg);border:2px solid var(--slot-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem;overflow:hidden;box-sizing:border-box}.slot:hover{border-color:var(--slot-border-hover);box-shadow:0 2px 4px #0000000d}.slot.selected{border-color:var(--slot-border-selected);background:var(--slot-bg-selected);box-shadow:0 0 0 3px var(--slot-bg-selected)}.slot.empty{border-style:dashed}.slot-label{font-size:.75rem;font-weight:600;color:var(--slot-label);text-transform:capitalize;text-align:left}.slot[data-position=middle] .slot-label,.slot[data-position=middle] .slot-panel-name,.slot[data-position=middle] .slot-empty-state{text-align:center}.slot[data-position=right] .slot-label,.slot[data-position=right] .slot-panel-name,.slot[data-position=right] .slot-empty-state{text-align:right}.slot-content{flex:1;display:flex;flex-direction:column;gap:.5rem;overflow-y:auto;min-height:0}.slot-panel-name{font-weight:600;color:var(--slot-content-text);font-size:.875rem}.slot-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--slot-preview-bg);border-radius:4px;border:1px solid var(--slot-preview-border);font-size:.75rem;color:var(--slot-preview-text)}.slot-empty-state{flex:1;display:flex;align-items:center;justify-content:center;color:var(--slot-empty-text);font-size:.875rem;font-style:italic;min-height:0}.slot-clear-btn{position:absolute;top:.25rem;right:.25rem;width:24px;height:24px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:4px;cursor:pointer;font-size:1.25rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease}.slot:hover .slot-clear-btn{opacity:1}.slot-clear-btn:hover{background:var(--clear-btn-hover);transform:scale(1.1)}.available-panels{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.75rem;width:75%;margin:0 auto}.available-panel{min-height:80px;padding:.75rem;background:var(--panel-bg);border:2px solid var(--panel-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem}.available-panel:hover{border-color:var(--panel-border-hover);box-shadow:0 2px 4px #0000000d;transform:translateY(-2px)}.available-panel.selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 3px var(--panel-bg-selected)}.available-panel.in-use{opacity:.5;border-style:dashed}.available-panel.in-use:hover{transform:translateY(0);opacity:.6}.panel-label{font-weight:600;color:var(--panel-label-text);font-size:.875rem}.panel-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--panel-preview-bg);border-radius:4px;font-size:.75rem;color:var(--panel-preview-text)}.selection-hint{padding:.75rem 1rem;background:var(--hint-bg);border:1px solid var(--hint-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}.available-panel.multi-selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 2px var(--panel-bg-selected)}.multi-select-badge{margin-left:.5rem;padding:.25rem .5rem;background:var(--panel-border-selected);color:#fff;font-size:.75rem;border-radius:12px;font-weight:400}.multi-select-hint{background:var(--panel-bg-selected);border-color:var(--panel-border-selected)}.slot.tab-group{border-style:solid}.group-content{position:relative;overflow-y:auto;min-height:0}.group-badge{font-size:.75rem;font-weight:600;color:var(--panel-border-selected);margin-bottom:.5rem}.group-panels{display:flex;flex-direction:column;gap:.25rem;flex:1;overflow-y:auto;min-height:0}.group-panel-label{font-size:.75rem;color:var(--slot-content-text);padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.slot-panel-label{font-weight:600;color:var(--slot-content-text);font-size:.875rem;text-align:center}.create-tab-group-btn{margin-top:.5rem;padding:.5rem .75rem;background:var(--panel-border-selected);color:#fff;border:none;border-radius:4px;font-size:.75rem;font-weight:600;cursor:pointer;transition:all .2s ease}.create-tab-group-btn:hover{transform:scale(1.05);box-shadow:0 2px 4px #0000001a}.tab-mode-toggle{position:absolute;top:.5rem;left:.5rem;padding:.25rem .5rem;background:var(--slot-bg);border:1px solid var(--slot-border);border-radius:4px;font-size:.85rem;cursor:pointer;transition:all .2s ease;z-index:10;opacity:.7;color:var(--slot-content-text)}.tab-mode-toggle svg{display:block}.tab-mode-toggle:hover{opacity:1;border-color:var(--slot-border-hover);background:var(--slot-preview-bg)}.tab-mode-toggle.active{opacity:1;background:var(--panel-border-selected);color:#fff;border-color:var(--panel-border-selected)}.tab-config-controls{margin-bottom:.5rem}.tab-config-label{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:var(--slot-label)}.tab-config-label select{padding:.25rem .5rem;border:1px solid var(--slot-border);border-radius:3px;background:var(--slot-bg);color:var(--slot-content-text);font-size:.7rem;cursor:pointer}.group-panel-item{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.group-panel-label{flex:1;font-size:.75rem;color:var(--slot-content-text);display:flex;align-items:center;gap:.25rem}.default-badge{color:var(--panel-border-selected);font-size:.9em}.remove-from-group-btn{width:18px;height:18px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:3px;cursor:pointer;font-size:1rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s ease}.remove-from-group-btn:hover{opacity:1;transform:scale(1.1)}.usage-hint{padding:.75rem 1rem;background:var(--slot-preview-bg);border:1px solid var(--slot-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}.snap-carousel-container{display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;gap:var(--snap-carousel-gap, 0px);padding:0;margin:0;width:100%;height:100%;background-color:var(--panel-background);box-sizing:border-box;position:relative;left:0;transform:none;container-type:inline-size;-ms-overflow-style:none;scrollbar-width:none}.snap-carousel-container::-webkit-scrollbar{display:none}.snap-carousel-panel{flex:0 0 auto;scroll-snap-align:start;scroll-snap-stop:always;width:var(--snap-carousel-panel-width, 33.33%);height:100%;box-sizing:border-box;overflow:hidden}@media (max-width: 540px){.snap-carousel-panel{min-width:280px}}
|
|
1
|
+
.animated-resizable-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.hybrid-panel{overflow:auto;height:100%;background-color:var(--panel-background)}.resize-handle{width:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:col-resize;position:relative;transition:background-color .2s,opacity .3s,width .3s}.resize-handle.collapsed{opacity:0;pointer-events:none}.resize-handle:hover{background-color:var(--panel-handle-hover)}.resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{height:100%;width:100%;display:flex;align-items:center;justify-content:center;position:relative}.collapse-toggle{position:absolute;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.animated-vertical-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.vertical-panel{overflow:auto;width:100%;background-color:var(--panel-background)}.panel-content-wrapper{width:100%;height:100%;overflow:auto}.vertical-resize-handle{height:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:row-resize;position:relative;transition:background-color .2s,opacity .3s,height .3s}.vertical-resize-handle.collapsed{opacity:0;pointer-events:none}.vertical-resize-handle:hover{background-color:var(--panel-handle-hover)}.vertical-resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{width:100%;height:100%;display:flex;align-items:center;justify-content:center;gap:8px;position:relative}.collapse-toggle{background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.collapse-toggle:hover:not(:disabled){background-color:var(--panel-button-hover);box-shadow:0 2px 6px #00000026}.collapse-toggle:active:not(:disabled){opacity:.8}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important}.panel-content-wrapper{flex:1;overflow:auto;will-change:opacity;box-sizing:border-box}.resize-handle.collapsed{width:0!important}.tab-group{display:flex;height:100%;width:100%;overflow:hidden}.tab-group.tab-position-top,.tab-group.tab-position-bottom{flex-direction:column}.tab-group.tab-position-left,.tab-group.tab-position-right{flex-direction:row}.tab-list{display:flex;background:var(--tab-list-bg, #f5f5f5);border-bottom:1px solid var(--tab-border, #ddd);gap:0;padding:0;flex-shrink:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.tab-list::-webkit-scrollbar{display:none}.tab-position-top .tab-list,.tab-position-bottom .tab-list{width:100%}.tab-list.centered{justify-content:flex-start}@media (min-width:0){.tab-list.centered{justify-content:center}.tab-list.centered:has(.tab-button:nth-child(n)){justify-content:flex-start}}.tab-position-bottom .tab-list{border-bottom:none;border-top:none}.tab-position-left .tab-list,.tab-position-right .tab-list{flex-direction:column;border-bottom:none;border-right:none;width:auto;min-width:120px}.tab-position-top .tab-button,.tab-position-bottom .tab-button{flex:1 1 0;min-width:40px;max-width:100%}.tab-button{background:var(--tab-bg, #fff);border:1px solid var(--tab-border, #ddd);border-radius:0;padding:8px 16px;cursor:pointer;font-size:14px;font-weight:500;color:var(--tab-text, #333);transition:all .2s ease;white-space:nowrap;display:flex;align-items:center;justify-content:center;gap:6px;height:40px;line-height:1;box-sizing:border-box;container-type:inline-size}.tab-position-top .tab-button{border-bottom:none;border-top:none;border-left:none}.tab-position-top .tab-button:last-child{border-right:none}.tab-position-bottom .tab-button{border-top:none;border-bottom:none;border-left:none}.tab-position-bottom .tab-button:last-child{border-right:none}.tab-position-left .tab-button{border-right:none;border-bottom:none}.tab-position-left .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-position-right .tab-button{border-left:none;border-bottom:none}.tab-position-right .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-icon{display:inline-flex;align-items:center;justify-content:center}.tab-label{display:none}@container (min-width: 250px){.tab-label{display:inline}.tab-icon{display:none}}.tab-button:hover{background:var(--tab-bg-hover, #f9f9f9)}.tab-button.active{background:var(--tab-bg-active, #007bff);color:var(--tab-text-active, #fff);border-color:var(--tab-border-active, #007bff)}.tab-button:focus-visible{outline:2px solid var(--tab-focus, #007bff);outline-offset:2px}.tab-content{flex:1;overflow:auto;background:var(--tab-content-bg, #fff)}.tab-group-empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--tab-empty-text, #999);font-style:italic}.three-panel-layout{height:100%;width:100%;display:flex;flex-direction:column;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item{display:flex;flex-direction:column;overflow:hidden;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item[data-edit-mode=true]{background-color:var(--panel-accent-bg);border-radius:12px}.three-panel-item.collapsible-panel{will-change:flex}.three-panel-item.collapsible-panel.animating{pointer-events:none}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important;max-width:0!important;width:0!important;overflow:hidden!important;visibility:hidden}.three-panel-item.middle-panel{flex:1;min-width:200px}.panel-content-wrapper{flex:1;overflow-x:hidden;overflow-y:auto;will-change:opacity;box-sizing:border-box}.resize-handle{position:relative;display:flex;align-items:center;justify-content:center;width:1px!important;cursor:col-resize;background:var(--panel-border);overflow:visible!important}.resize-handle:before{content:"";position:absolute;top:0;right:-10px;bottom:0;left:-10px;background:transparent}.resize-handle:after{content:"";position:absolute;top:0;right:-10px;bottom:0;left:-10px;background:var(--panel-handle);opacity:0;transition:opacity .2s ease;z-index:-1}.resize-handle:hover:after{opacity:1}.resize-handle:hover{background:var(--panel-handle-hover)}.resize-handle:active:after{opacity:1;background:var(--panel-handle-active)}.resize-handle:active{background:var(--panel-handle-active)}.resize-handle.collapsed{width:0!important;visibility:hidden}.resize-handle.left-handle.collapsed{margin-right:-1px}.resize-handle.right-handle.collapsed{margin-left:-1px}.handle-bar{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;z-index:2}.collapse-toggle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:20px;height:40px;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);border-radius:4px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;color:var(--panel-button-icon);transition:all .2s ease;z-index:10;padding:0;line-height:1}.collapse-toggle:hover{background:var(--panel-button-hover)}.collapse-toggle:active{opacity:.8}.collapse-toggle:disabled{opacity:.5;cursor:not-allowed}@media (max-width:768px){.resize-handle:before{left:-8px;right:-8px}.resize-handle:after{left:-8px;right:-8px}.collapse-toggle{width:24px;height:48px;font-size:14px}}.snap-carousel-container{display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;gap:var(--snap-carousel-gap, 0px);padding:0;margin:0;width:100%;height:100%;background-color:var(--panel-background);box-sizing:border-box;position:relative;left:0;transform:none;container-type:inline-size;-ms-overflow-style:none;scrollbar-width:none}.snap-carousel-container::-webkit-scrollbar{display:none}.snap-carousel-panel{flex:0 0 auto;scroll-snap-align:start;scroll-snap-stop:always;width:var(--snap-carousel-panel-width, 33.33%);height:100%;box-sizing:border-box;overflow:hidden}@media (max-width:540px){.snap-carousel-panel{min-width:280px}}@keyframes wiggle{0%{transform:rotate(0)}25%{transform:rotate(1deg)}50%{transform:rotate(0)}75%{transform:rotate(-1deg)}to{transform:rotate(0)}}@keyframes scaleIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}@keyframes pulse{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 0 8px #3b82f600}}.editable-panel-layout{position:relative;width:100%;height:100%}.editable-panel-layout.edit-mode-active{background:#0000000d}[data-slot][data-edit-mode=true]{transform:scale(.95);transform-origin:center center;cursor:grab;will-change:transform;transition:transform .3s cubic-bezier(.4,0,.2,1)}[data-slot][data-edit-mode=true]:active{cursor:grabbing}[data-slot][data-dragging=true]{cursor:grabbing!important;transform:scale(.95)!important;transition:none!important}.edit-mode-active [data-slot][data-edit-mode=true]{transform-origin:center center}.slot-with-overlay{position:relative;width:100%;height:100%}.slot-with-overlay.dragging{opacity:0;pointer-events:none}.slot-edit-overlay{position:absolute;top:0;right:0;bottom:0;left:0;background:transparent;border:none;pointer-events:auto;cursor:grab;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center}.slot-edit-overlay:hover{background:#3b82f608}.slot-edit-overlay:active{cursor:grabbing}.drag-indicator,.slot-position-label{display:none}.edit-mode-toggle{position:absolute;top:16px;right:16px;z-index:1000;padding:8px 16px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;box-shadow:0 2px 4px #0000001a}.edit-mode-toggle:hover{background:#f9fafb;box-shadow:0 4px 6px #0000001a}.edit-mode-toggle.active{background:#3b82f6;color:#fff;border-color:#2563eb}.edit-mode-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#0000004d;z-index:998;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.edit-mode-configurator{position:relative;z-index:999;padding:20px;animation:scaleIn .3s ease}.panel-slot{position:relative;min-height:120px;border:2px dashed transparent;border-radius:12px;transition:all .3s ease;padding:12px}.panel-slot.edit-mode{border-color:#d1d5db;background:#ffffff80}.panel-slot.drag-over{border-color:#3b82f6;background:#3b82f61a;box-shadow:0 0 0 4px #3b82f61a}.panel-slot.empty{display:flex;align-items:center;justify-content:center;color:#9ca3af;font-size:14px}.draggable-panel{position:relative;padding:12px 16px;margin:8px 0;border-radius:8px;background:#fff;border:1px solid #e5e7eb;cursor:grab;transition:all .2s ease;-webkit-user-select:none;user-select:none}.draggable-panel:hover{border-color:#3b82f6;box-shadow:0 2px 8px #0000001a}.draggable-panel.dragging{opacity:.5;cursor:grabbing}.draggable-panel.edit-mode{animation:wiggle .4s ease-in-out infinite;transform-origin:center}.draggable-panel.edit-mode:nth-child(odd){animation-delay:.1s}.draggable-panel.edit-mode:nth-child(2n){animation-delay:.2s}.remove-button{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#ef4444;color:#fff;border:2px solid white;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;font-weight:700;opacity:0;transform:scale(0);transition:all .2s ease;z-index:10;box-shadow:0 2px 4px #0003}.draggable-panel.edit-mode .remove-button{opacity:1;transform:scale(1);animation:pulse 2s infinite}.remove-button:hover{background:#dc2626;transform:scale(1.1)}.panel-label{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500;color:#374151}.panel-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.drag-handle{display:none;width:24px;height:24px;opacity:.4;cursor:grab}.edit-mode .drag-handle{display:flex;align-items:center;justify-content:center}.drag-overlay{padding:12px 16px;border-radius:8px;background:#fff;border:2px solid #3b82f6;box-shadow:0 8px 16px #0003;cursor:grabbing;opacity:.9}.panel-group{border:2px solid #e5e7eb;border-radius:12px;padding:8px;background:#fff}.panel-group.edit-mode{animation:wiggle .5s ease-in-out infinite;border-color:#3b82f6}.panel-group-header{display:flex;justify-content:space-between;align-items:center;padding:8px;border-bottom:1px solid #e5e7eb;margin-bottom:8px}.panel-group-title{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase}.available-panels{background:#fff;border-radius:12px;padding:16px;box-shadow:0 4px 6px #0000001a}.available-panels-title{font-size:16px;font-weight:600;color:#374151;margin-bottom:12px}.available-panels-list{display:flex;flex-direction:column;gap:8px}.slot-label{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center;gap:8px}.slot-indicator{width:8px;height:8px;border-radius:50%;background:#d1d5db}.slot-indicator.active{background:#3b82f6}.action-buttons{position:fixed;bottom:24px;left:50%;transform:translate(-50%);z-index:1001;display:flex;gap:12px;padding:12px 24px;background:#fff;border-radius:12px;box-shadow:0 8px 16px #00000026;animation:scaleIn .3s ease}.action-button{padding:10px 20px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease}.action-button:hover{background:#f9fafb}.action-button.primary{background:#3b82f6;color:#fff;border-color:#2563eb}.action-button.primary:hover{background:#2563eb}@media (max-width:768px){.edit-mode-toggle{top:8px;right:8px;padding:6px 12px;font-size:12px}.action-buttons{bottom:16px;padding:10px 16px}.action-button{padding:8px 16px;font-size:13px}}@media (prefers-color-scheme:dark){.edit-mode-toggle{background:#1f2937;color:#f9fafb;border-color:#374151}.edit-mode-toggle:hover{background:#374151}.draggable-panel{background:#1f2937;color:#f9fafb;border-color:#374151}.available-panels{background:#1f2937}.panel-label{color:#f9fafb}.action-buttons{background:#1f2937}.action-button{background:#1f2937;color:#f9fafb;border-color:#374151}.action-button:hover{background:#374151}}.panel-configurator{display:flex;flex-direction:column;gap:2rem;padding:1.5rem;background:var(--configurator-bg);border-radius:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.configurator-section{display:flex;flex-direction:column;gap:1rem}.section-title{margin:0;font-size:.875rem;font-weight:600;color:var(--configurator-title);text-transform:uppercase;letter-spacing:.05em;text-align:center}.slots-container{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;width:75%;margin:0 auto;align-items:start}.slot{position:relative;aspect-ratio:1 / 1.3;width:100%;padding:2.5rem 1rem 1rem;background:var(--slot-bg);border:2px solid var(--slot-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem;overflow:hidden;box-sizing:border-box}.slot:hover{border-color:var(--slot-border-hover);box-shadow:0 2px 4px #0000000d}.slot.selected{border-color:var(--slot-border-selected);background:var(--slot-bg-selected);box-shadow:0 0 0 3px var(--slot-bg-selected)}.slot.empty{border-style:dashed}.slot-label{font-size:.75rem;font-weight:600;color:var(--slot-label);text-transform:capitalize;text-align:left}.slot[data-position=middle] .slot-label,.slot[data-position=middle] .slot-panel-name,.slot[data-position=middle] .slot-empty-state{text-align:center}.slot[data-position=right] .slot-label,.slot[data-position=right] .slot-panel-name,.slot[data-position=right] .slot-empty-state{text-align:right}.slot-content{flex:1;display:flex;flex-direction:column;gap:.5rem;overflow-y:auto;min-height:0}.slot-panel-name{font-weight:600;color:var(--slot-content-text);font-size:.875rem}.slot-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--slot-preview-bg);border-radius:4px;border:1px solid var(--slot-preview-border);font-size:.75rem;color:var(--slot-preview-text)}.slot-empty-state{flex:1;display:flex;align-items:center;justify-content:center;color:var(--slot-empty-text);font-size:.875rem;font-style:italic;min-height:0}.slot-clear-btn{position:absolute;top:.25rem;right:.25rem;width:24px;height:24px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:4px;cursor:pointer;font-size:1.25rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease}.slot:hover .slot-clear-btn{opacity:1}.slot-clear-btn:hover{background:var(--clear-btn-hover);transform:scale(1.1)}.available-panels{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.75rem;width:75%;margin:0 auto}.available-panel{min-height:80px;padding:.75rem;background:var(--panel-bg);border:2px solid var(--panel-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem}.available-panel:hover{border-color:var(--panel-border-hover);box-shadow:0 2px 4px #0000000d;transform:translateY(-2px)}.available-panel.selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 3px var(--panel-bg-selected)}.available-panel.in-use{opacity:.5;border-style:dashed}.available-panel.in-use:hover{transform:translateY(0);opacity:.6}.panel-label{font-weight:600;color:var(--panel-label-text);font-size:.875rem}.panel-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--panel-preview-bg);border-radius:4px;font-size:.75rem;color:var(--panel-preview-text)}.selection-hint{padding:.75rem 1rem;background:var(--hint-bg);border:1px solid var(--hint-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}.available-panel.multi-selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 2px var(--panel-bg-selected)}.multi-select-badge{margin-left:.5rem;padding:.25rem .5rem;background:var(--panel-border-selected);color:#fff;font-size:.75rem;border-radius:12px;font-weight:400}.multi-select-hint{background:var(--panel-bg-selected);border-color:var(--panel-border-selected)}.slot.tab-group{border-style:solid}.group-content{position:relative;overflow-y:auto;min-height:0}.group-badge{font-size:.75rem;font-weight:600;color:var(--panel-border-selected);margin-bottom:.5rem}.group-panels{display:flex;flex-direction:column;gap:.25rem;flex:1;overflow-y:auto;min-height:0}.group-panel-label{font-size:.75rem;color:var(--slot-content-text);padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.slot-panel-label{font-weight:600;color:var(--slot-content-text);font-size:.875rem;text-align:center}.create-tab-group-btn{margin-top:.5rem;padding:.5rem .75rem;background:var(--panel-border-selected);color:#fff;border:none;border-radius:4px;font-size:.75rem;font-weight:600;cursor:pointer;transition:all .2s ease}.create-tab-group-btn:hover{transform:scale(1.05);box-shadow:0 2px 4px #0000001a}.tab-mode-toggle{position:absolute;top:.5rem;left:.5rem;padding:.25rem .5rem;background:var(--slot-bg);border:1px solid var(--slot-border);border-radius:4px;font-size:.85rem;cursor:pointer;transition:all .2s ease;z-index:10;opacity:.7;color:var(--slot-content-text)}.tab-mode-toggle svg{display:block}.tab-mode-toggle:hover{opacity:1;border-color:var(--slot-border-hover);background:var(--slot-preview-bg)}.tab-mode-toggle.active{opacity:1;background:var(--panel-border-selected);color:#fff;border-color:var(--panel-border-selected)}.tab-config-controls{margin-bottom:.5rem}.tab-config-label{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:var(--slot-label)}.tab-config-label select{padding:.25rem .5rem;border:1px solid var(--slot-border);border-radius:3px;background:var(--slot-bg);color:var(--slot-content-text);font-size:.7rem;cursor:pointer}.group-panel-item{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.group-panel-label{flex:1;font-size:.75rem;color:var(--slot-content-text);display:flex;align-items:center;gap:.25rem}.default-badge{color:var(--panel-border-selected);font-size:.9em}.remove-from-group-btn{width:18px;height:18px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:3px;cursor:pointer;font-size:1rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s ease}.remove-from-group-btn:hover{opacity:1;transform:scale(1.1)}.usage-hint{padding:.75rem 1rem;background:var(--slot-preview-bg);border:1px solid var(--slot-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { EditableConfigurablePanelLayout } from '@
|
|
2
|
-
import { EditableConfigurablePanelLayoutProps } from '@
|
|
3
|
-
import { mapThemeToPanelVars } from '@
|
|
4
|
-
import { mapThemeToTabVars } from '@
|
|
5
|
-
import { PanelDefinition } from '@
|
|
6
|
-
import { PanelDefinitionWithContent } from '@
|
|
7
|
-
import { PanelGroup } from '@
|
|
8
|
-
import { PanelLayout } from '@
|
|
9
|
-
import { PanelSlot } from '@
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
1
|
+
import { EditableConfigurablePanelLayout } from '@principal-ade/panels';
|
|
2
|
+
import { EditableConfigurablePanelLayoutProps } from '@principal-ade/panels';
|
|
3
|
+
import { mapThemeToPanelVars } from '@principal-ade/panels';
|
|
4
|
+
import { mapThemeToTabVars } from '@principal-ade/panels';
|
|
5
|
+
import { PanelDefinition } from '@principal-ade/panels';
|
|
6
|
+
import { PanelDefinitionWithContent } from '@principal-ade/panels';
|
|
7
|
+
import { PanelGroup } from '@principal-ade/panels';
|
|
8
|
+
import { PanelLayout } from '@principal-ade/panels';
|
|
9
|
+
import { PanelSlot } from '@principal-ade/panels';
|
|
10
|
+
import { ResponsiveConfigurablePanelLayout } from '@principal-ade/panels';
|
|
11
|
+
import { ResponsiveConfigurablePanelLayoutProps } from '@principal-ade/panels';
|
|
12
|
+
import { TabsConfig } from '@principal-ade/panels';
|
|
13
|
+
import { Theme } from '@principal-ade/panels';
|
|
14
|
+
import { TilesConfig } from '@principal-ade/panels';
|
|
13
15
|
|
|
14
16
|
/**
|
|
15
17
|
* Built-in workspace IDs
|
|
@@ -163,6 +165,10 @@ export declare interface RepositoryWorkspaceState {
|
|
|
163
165
|
};
|
|
164
166
|
}
|
|
165
167
|
|
|
168
|
+
export { ResponsiveConfigurablePanelLayout }
|
|
169
|
+
|
|
170
|
+
export { ResponsiveConfigurablePanelLayoutProps }
|
|
171
|
+
|
|
166
172
|
export { TabsConfig }
|
|
167
173
|
|
|
168
174
|
export { Theme }
|
package/dist/index.esm.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import './index.css';var $ = Object.defineProperty;
|
|
2
2
|
var D = (n, e, t) => e in n ? $(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
|
|
3
|
-
var
|
|
4
|
-
import { EditableConfigurablePanelLayout as q,
|
|
5
|
-
import { useState as b, useRef as
|
|
3
|
+
var C = (n, e, t) => D(n, typeof e != "symbol" ? e + "" : e, t);
|
|
4
|
+
import { EditableConfigurablePanelLayout as q, ResponsiveConfigurablePanelLayout as K, mapThemeToPanelVars as X, mapThemeToTabVars as M } from "@principal-ade/panels";
|
|
5
|
+
import { useState as b, useRef as T, useEffect as R, useCallback as d } from "react";
|
|
6
6
|
class F {
|
|
7
7
|
constructor() {
|
|
8
|
-
|
|
8
|
+
C(this, "storageKey", "panel-layouts");
|
|
9
9
|
}
|
|
10
10
|
async load(e) {
|
|
11
11
|
try {
|
|
@@ -32,7 +32,7 @@ function x(n) {
|
|
|
32
32
|
defaultSizes: t,
|
|
33
33
|
panelType: a,
|
|
34
34
|
adapter: s = new F()
|
|
35
|
-
} = n, [f, u] = b(t), [y, E] = b(n.collapsed), S =
|
|
35
|
+
} = n, [f, u] = b(t), [y, E] = b(n.collapsed), S = T(n.collapsed), v = T({}), h = T(null), k = T(null);
|
|
36
36
|
R(() => {
|
|
37
37
|
(async () => {
|
|
38
38
|
const c = await s.load(e);
|
|
@@ -55,7 +55,7 @@ function x(n) {
|
|
|
55
55
|
return t.right;
|
|
56
56
|
},
|
|
57
57
|
[t]
|
|
58
|
-
), W =
|
|
58
|
+
), W = T(t);
|
|
59
59
|
R(() => {
|
|
60
60
|
("left" in t && t.left !== W.current.left || "middle" in t && "middle" in W.current && t.middle !== W.current.middle || "right" in t && t.right !== W.current.right) && (u(t), m(t), W.current = t);
|
|
61
61
|
}, [t, m]), R(() => {
|
|
@@ -140,8 +140,8 @@ function x(n) {
|
|
|
140
140
|
}
|
|
141
141
|
class V {
|
|
142
142
|
constructor() {
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
C(this, "PRESETS_KEY", "panel-layouts:workspace-presets");
|
|
144
|
+
C(this, "REPO_STATE_PREFIX", "panel-layouts:repo-state:");
|
|
145
145
|
}
|
|
146
146
|
/**
|
|
147
147
|
* Load all user-created workspace presets
|
|
@@ -608,7 +608,7 @@ class p {
|
|
|
608
608
|
Object.keys(e).length;
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
|
-
|
|
611
|
+
C(p, "adapter", new V());
|
|
612
612
|
function J(n = {}) {
|
|
613
613
|
const { repositoryKey: e, autoInitialize: t = !1, defaultWorkspaceId: a } = n, [s, f] = b(
|
|
614
614
|
{}
|
|
@@ -750,9 +750,10 @@ export {
|
|
|
750
750
|
q as EditableConfigurablePanelLayout,
|
|
751
751
|
F as LocalStoragePersistenceAdapter,
|
|
752
752
|
V as LocalStorageWorkspaceAdapter,
|
|
753
|
+
K as ResponsiveConfigurablePanelLayout,
|
|
753
754
|
p as WorkspaceLayoutService,
|
|
754
|
-
|
|
755
|
-
|
|
755
|
+
X as mapThemeToPanelVars,
|
|
756
|
+
M as mapThemeToTabVars,
|
|
756
757
|
x as usePanelPersistence,
|
|
757
758
|
J as useWorkspace
|
|
758
759
|
};
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/hooks/usePanelPersistence.ts","../src/services/LocalStorageWorkspaceAdapter.ts","../src/services/WorkspaceLayoutService.ts","../src/hooks/useWorkspace.ts"],"sourcesContent":["/**\n * usePanelPersistence Hook\n *\n * Extracted and adapted from electron-app/src/renderer/hooks/usePanelPersistence.ts\n * Manages saving and loading panel sizes and collapsed states with debounced persistence.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type {\n UsePanelPersistenceOptions,\n PanelSizes,\n TwoPanelSizes,\n PanelCollapsed,\n PanelPersistence,\n PersistenceAdapter,\n} from '../types/persistence.types';\n\n/**\n * Default localStorage-based persistence adapter for web applications\n */\nexport class LocalStoragePersistenceAdapter implements PersistenceAdapter {\n private storageKey = 'panel-layouts';\n\n async load(viewKey: string) {\n try {\n const stored = localStorage.getItem(`${this.storageKey}:${viewKey}`);\n return stored ? JSON.parse(stored) : null;\n } catch (error) {\n console.error(`Failed to load panel layout for ${viewKey}:`, error);\n return null;\n }\n }\n\n async save(viewKey: string, state: { sizes: PanelSizes | TwoPanelSizes }) {\n try {\n localStorage.setItem(\n `${this.storageKey}:${viewKey}`,\n JSON.stringify(state),\n );\n } catch (error) {\n console.error(`Failed to save panel layout for ${viewKey}:`, error);\n }\n }\n}\n\n/**\n * Hook for persisting panel layouts across sessions\n *\n * Manages panel sizes and collapsed states with automatic debounced saving.\n * Supports both 2-panel and 3-panel layouts.\n *\n * @param options - Configuration options for persistence\n * @returns Panel state and handlers for resizing and collapsing\n *\n * @example\n * ```tsx\n * const persistence = usePanelPersistence({\n * viewKey: 'my-app',\n * defaultSizes: { left: 30, middle: 70, right: 0 },\n * collapsed: { left: false, right: true },\n * panelType: 'three-panel',\n * });\n *\n * <ConfigurablePanelLayout\n * {...otherProps}\n * defaultSizes={persistence.sizes}\n * collapsed={persistence.collapsed}\n * onPanelResize={persistence.handlePanelResize}\n * />\n * ```\n */\nexport function usePanelPersistence(\n options: UsePanelPersistenceOptions,\n): PanelPersistence {\n const {\n viewKey,\n defaultSizes,\n panelType,\n adapter = new LocalStoragePersistenceAdapter(),\n } = options;\n\n const [sizes, setSizes] = useState(defaultSizes);\n const [collapsed, setCollapsed] = useState(options.collapsed);\n const prevCollapsedRef = useRef(options.collapsed);\n const lastNonZeroSizesRef = useRef<Partial<PanelSizes & TwoPanelSizes>>({});\n const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingPersistSizesRef = useRef<typeof sizes | null>(null);\n\n // Load saved state on mount\n useEffect(() => {\n const loadSavedState = async () => {\n const saved = await adapter.load(viewKey);\n if (saved?.sizes) {\n setSizes(saved.sizes as typeof sizes);\n updateLastNonZeroSizes(saved.sizes);\n }\n };\n loadSavedState();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [viewKey]);\n\n const updateLastNonZeroSizes = useCallback(\n (incomingSizes: PanelSizes | TwoPanelSizes) => {\n if ('left' in incomingSizes && incomingSizes.left > 0) {\n lastNonZeroSizesRef.current.left = incomingSizes.left;\n }\n\n if ('middle' in incomingSizes && incomingSizes.middle > 0) {\n lastNonZeroSizesRef.current.middle = incomingSizes.middle;\n }\n\n if ('right' in incomingSizes && incomingSizes.right > 0) {\n lastNonZeroSizesRef.current.right = incomingSizes.right;\n }\n },\n [],\n );\n\n const getFallbackSize = useCallback(\n (panel: 'left' | 'right') => {\n const storedSize = lastNonZeroSizesRef.current[panel];\n if (storedSize && storedSize > 0) {\n return storedSize;\n }\n\n if (panel === 'left' && 'left' in defaultSizes && defaultSizes.left > 0) {\n return defaultSizes.left;\n }\n\n if (\n panel === 'right' &&\n 'right' in defaultSizes &&\n defaultSizes.right > 0\n ) {\n return defaultSizes.right;\n }\n\n return undefined;\n },\n [defaultSizes],\n );\n\n // Update sizes when defaultSizes changes (parent has loaded preferences)\n const prevDefaultSizesRef = useRef(defaultSizes);\n useEffect(() => {\n // Only update if the actual values changed, not just the reference\n const hasChanged =\n ('left' in defaultSizes &&\n defaultSizes.left !== prevDefaultSizesRef.current.left) ||\n ('middle' in defaultSizes &&\n 'middle' in prevDefaultSizesRef.current &&\n defaultSizes.middle !==\n (prevDefaultSizesRef.current as PanelSizes).middle) ||\n ('right' in defaultSizes &&\n defaultSizes.right !== prevDefaultSizesRef.current.right);\n\n if (hasChanged) {\n setSizes(defaultSizes);\n updateLastNonZeroSizes(defaultSizes);\n prevDefaultSizesRef.current = defaultSizes;\n }\n }, [defaultSizes, updateLastNonZeroSizes]);\n\n // Sync with parent's collapsed state (e.g., from titlebar buttons)\n useEffect(() => {\n const leftChanged =\n options.collapsed.left !== prevCollapsedRef.current.left;\n const rightChanged =\n 'right' in options.collapsed &&\n 'right' in prevCollapsedRef.current &&\n (options.collapsed as PanelCollapsed).right !==\n (prevCollapsedRef.current as PanelCollapsed).right;\n\n if (leftChanged || rightChanged) {\n setCollapsed(options.collapsed);\n prevCollapsedRef.current = { ...options.collapsed };\n }\n }, [options.collapsed.left, (options.collapsed as PanelCollapsed).right]);\n\n // Save preferences helper (only saves sizes, not collapsed state)\n const savePreferences = useCallback(\n async (newSizes: typeof sizes) => {\n try {\n await adapter.save(viewKey, { sizes: newSizes });\n } catch (error) {\n console.error(\n `Failed to save panel preferences for ${viewKey}:`,\n error,\n );\n }\n },\n [viewKey, adapter],\n );\n\n // Handle panel resize (debounced)\n const handlePanelResize = useCallback(\n (newSizes: typeof sizes) => {\n const sanitizedSizes = { ...newSizes } as typeof newSizes;\n let shouldPersist = true;\n\n if ('left' in newSizes) {\n const leftCollapsed = Boolean((collapsed as PanelCollapsed)?.left);\n const leftSize = newSizes.left;\n\n if (leftCollapsed) {\n const fallback = getFallbackSize('left');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.left = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (leftSize === 0) {\n shouldPersist = false;\n }\n }\n\n if (panelType === 'three-panel' && 'right' in newSizes) {\n const rightCollapsed = Boolean((collapsed as PanelCollapsed)?.right);\n const rightSize = newSizes.right;\n\n if (rightCollapsed) {\n const fallback = getFallbackSize('right');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.right = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (rightSize === 0) {\n shouldPersist = false;\n }\n }\n\n setSizes(sanitizedSizes);\n updateLastNonZeroSizes(sanitizedSizes);\n\n if (saveTimeoutRef.current) {\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n\n if (!shouldPersist) {\n pendingPersistSizesRef.current = null;\n return;\n }\n\n pendingPersistSizesRef.current = sanitizedSizes;\n saveTimeoutRef.current = setTimeout(() => {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n saveTimeoutRef.current = null;\n }, 500);\n },\n [\n collapsed,\n getFallbackSize,\n panelType,\n savePreferences,\n updateLastNonZeroSizes,\n ],\n );\n\n // Cleanup: save any pending changes on unmount\n useEffect(() => {\n return () => {\n if (saveTimeoutRef.current) {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n };\n }, [savePreferences]);\n\n // Collapse/expand handlers - no-ops because state is controlled by parent\n // The parent manages collapsed state via titlebar buttons and passes it down as props\n const handleLeftCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleLeftExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n if (panelType === 'three-panel') {\n return {\n type: 'three-panel',\n sizes: sizes as PanelSizes,\n collapsed: collapsed as PanelCollapsed,\n handlePanelResize: handlePanelResize as (sizes: PanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n handleRightCollapseComplete,\n handleRightExpandComplete,\n };\n } else {\n return {\n type: 'two-panel',\n sizes: sizes as TwoPanelSizes,\n collapsed: collapsed as { left?: boolean },\n handlePanelResize: handlePanelResize as (sizes: TwoPanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n };\n }\n}\n","/**\n * LocalStorageWorkspaceAdapter - Browser localStorage implementation\n * Stores workspace presets and repository states in browser localStorage\n */\n\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n} from '../types/workspace.types';\n\nexport class LocalStorageWorkspaceAdapter implements WorkspaceStorageAdapter {\n private readonly PRESETS_KEY = 'panel-layouts:workspace-presets';\n private readonly REPO_STATE_PREFIX = 'panel-layouts:repo-state:';\n\n /**\n * Load all user-created workspace presets\n */\n async loadWorkspacePresets(): Promise<Record<string, WorkspaceLayout>> {\n try {\n const stored = localStorage.getItem(this.PRESETS_KEY);\n if (!stored) {\n return {};\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error('Failed to load workspace presets:', error);\n return {};\n }\n }\n\n /**\n * Save workspace presets\n */\n async saveWorkspacePresets(\n presets: Record<string, WorkspaceLayout>,\n ): Promise<void> {\n try {\n localStorage.setItem(this.PRESETS_KEY, JSON.stringify(presets));\n } catch (error) {\n console.error('Failed to save workspace presets:', error);\n throw error;\n }\n }\n\n /**\n * Load repository-specific workspace state\n */\n async loadRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n const stored = localStorage.getItem(key);\n if (!stored) {\n return null;\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error(\n `Failed to load repository state for ${repositoryKey}:`,\n error,\n );\n return null;\n }\n }\n\n /**\n * Save repository-specific workspace state\n */\n async saveRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n localStorage.setItem(key, JSON.stringify(state));\n } catch (error) {\n console.error(\n `Failed to save repository state for ${repositoryKey}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Load all repository states\n */\n async loadAllRepositoryStates(): Promise<\n Record<string, RepositoryWorkspaceState>\n > {\n try {\n const allStates: Record<string, RepositoryWorkspaceState> = {};\n\n // Iterate through all localStorage keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(this.REPO_STATE_PREFIX)) {\n const repositoryKey = key.substring(this.REPO_STATE_PREFIX.length);\n const stored = localStorage.getItem(key);\n if (stored) {\n try {\n allStates[repositoryKey] = JSON.parse(stored);\n } catch (parseError) {\n console.error(\n `Failed to parse repository state for ${repositoryKey}:`,\n parseError,\n );\n }\n }\n }\n }\n\n return allStates;\n } catch (error) {\n console.error('Failed to load all repository states:', error);\n return {};\n }\n }\n}\n","/**\n * WorkspaceLayoutService - Manages workspace layout presets\n * Extracted from electron-app/src/renderer/services/WorkspaceLayoutService.ts\n *\n * This service handles CRUD operations for workspace layouts (panel configuration presets)\n * and provides utilities for comparing layouts and managing built-in workspaces.\n */\n\nimport type { PanelLayout } from '@a24z/panels';\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\nimport { LocalStorageWorkspaceAdapter } from './LocalStorageWorkspaceAdapter';\n\nexport class WorkspaceLayoutService {\n private static adapter: WorkspaceStorageAdapter =\n new LocalStorageWorkspaceAdapter();\n\n /**\n * Configure a custom storage adapter (for Electron IPC or remote storage)\n */\n static setAdapter(adapter: WorkspaceStorageAdapter): void {\n this.adapter = adapter;\n }\n\n /**\n * Get all workspace layouts (including built-in)\n */\n static async getWorkspaceLayouts(): Promise<Record<string, WorkspaceLayout>> {\n const builtInWorkspaces = this.getBuiltInWorkspaceLayouts();\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Merge built-in and user workspaces, built-in takes precedence\n return {\n ...userWorkspaces,\n ...builtInWorkspaces,\n };\n }\n\n /**\n * Get a specific workspace layout by ID\n */\n static async getWorkspaceLayout(id: string): Promise<WorkspaceLayout | null> {\n const workspaces = await this.getWorkspaceLayouts();\n return workspaces[id] || null;\n }\n\n /**\n * Create a new workspace layout\n */\n static async createWorkspaceLayout(\n options: CreateWorkspaceOptions,\n ): Promise<WorkspaceLayout> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const id = this.generateWorkspaceId(options.name);\n\n const workspace: WorkspaceLayout = {\n id,\n name: options.name,\n description: options.description,\n layout: options.layout,\n defaultSizes: options.defaultSizes,\n defaultCollapsed: options.defaultCollapsed,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n isBuiltIn: false,\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: workspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return workspace;\n }\n\n /**\n * Update an existing workspace layout\n */\n static async updateWorkspaceLayout(\n id: string,\n updates: UpdateWorkspaceOptions,\n ): Promise<WorkspaceLayout | null> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const existingWorkspace = userWorkspaces[id];\n\n if (!existingWorkspace) {\n console.error(`Workspace layout ${id} not found`);\n return null;\n }\n\n // Don't allow updating built-in workspaces\n if (existingWorkspace.isBuiltIn) {\n console.error(`Cannot update built-in workspace layout ${id}`);\n return null;\n }\n\n const updatedWorkspace: WorkspaceLayout = {\n ...existingWorkspace,\n ...updates,\n id, // Preserve ID\n createdAt: existingWorkspace.createdAt, // Preserve creation time\n updatedAt: Date.now(),\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: updatedWorkspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return updatedWorkspace;\n }\n\n /**\n * Delete a workspace layout\n */\n static async deleteWorkspaceLayout(id: string): Promise<boolean> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const workspace = userWorkspaces[id];\n\n if (!workspace) {\n console.error(`Workspace layout ${id} not found`);\n return false;\n }\n\n // Don't allow deleting built-in workspaces\n if (workspace.isBuiltIn) {\n console.error(`Cannot delete built-in workspace layout ${id}`);\n return false;\n }\n\n const updatedPresets = { ...userWorkspaces };\n delete updatedPresets[id];\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return true;\n }\n\n /**\n * Get repository state (which workspace + current sizes/collapsed)\n */\n static async getRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n return await this.adapter.loadRepositoryState(repositoryKey);\n }\n\n /**\n * Set repository state (which workspace + current sizes/collapsed/layout)\n */\n static async setRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n await this.adapter.saveRepositoryState(repositoryKey, state);\n }\n\n /**\n * Update only sizes in repository state\n */\n static async updateRepositorySizes(\n repositoryKey: string,\n sizes: { left: number; middle: number; right: number },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes,\n });\n }\n\n /**\n * Update only collapsed state in repository state\n */\n static async updateRepositoryCollapsed(\n repositoryKey: string,\n collapsed: { left?: boolean; right?: boolean },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n collapsed,\n });\n }\n\n /**\n * Check if repository state differs from workspace defaults\n */\n static hasStateDeviation(\n repoState: {\n workspaceId: string | null;\n sizes: { left: number; middle: number; right: number };\n collapsed: { left?: boolean; right?: boolean };\n },\n workspace: WorkspaceLayout,\n ): {\n hasSizeDeviation: boolean;\n hasCollapsedDeviation: boolean;\n } {\n const hasSizeDeviation = workspace.defaultSizes\n ? JSON.stringify(repoState.sizes) !==\n JSON.stringify(workspace.defaultSizes)\n : false;\n\n const hasCollapsedDeviation = workspace.defaultCollapsed\n ? JSON.stringify(repoState.collapsed) !==\n JSON.stringify(workspace.defaultCollapsed)\n : false;\n\n return { hasSizeDeviation, hasCollapsedDeviation };\n }\n\n /**\n * Update workspace defaults from repository state\n */\n static async updateWorkspaceFromRepositoryState(\n workspaceId: string,\n repositoryKey: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n const repoState = await this.getRepositoryState(repositoryKey);\n\n if (!workspace || !repoState || workspace.isBuiltIn) {\n console.error(\n 'Cannot update built-in workspace or workspace/state not found',\n );\n return;\n }\n\n await this.updateWorkspaceLayout(workspaceId, {\n defaultSizes: repoState.sizes,\n defaultCollapsed: repoState.collapsed,\n });\n }\n\n /**\n * Reset repository state to workspace defaults\n */\n static async resetRepositoryToWorkspaceDefaults(\n repositoryKey: string,\n workspaceId: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || { left: false, right: false },\n });\n }\n\n /**\n * Check if a layout matches a workspace layout\n */\n static isLayoutMatchingWorkspace(\n layout: PanelLayout,\n workspace: WorkspaceLayout,\n ): boolean {\n return this.areLayoutsEqual(layout, workspace.layout);\n }\n\n /**\n * Find workspace ID that matches the given layout\n */\n static async findMatchingWorkspace(\n layout: PanelLayout,\n ): Promise<string | null> {\n const workspaces = await this.getWorkspaceLayouts();\n\n for (const [id, workspace] of Object.entries(workspaces)) {\n if (this.isLayoutMatchingWorkspace(layout, workspace)) {\n return id;\n }\n }\n\n return null;\n }\n\n /**\n * Deep comparison of two panel layouts\n */\n private static areLayoutsEqual(\n layout1: PanelLayout,\n layout2: PanelLayout,\n ): boolean {\n return JSON.stringify(layout1) === JSON.stringify(layout2);\n }\n\n /**\n * Generate a unique ID from a workspace name\n */\n private static generateWorkspaceId(name: string): string {\n const base = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n return `${base}-${Date.now()}`;\n }\n\n /**\n * Get built-in workspace layouts\n */\n static getBuiltInWorkspaceLayouts(): Record<string, WorkspaceLayout> {\n const now = Date.now();\n\n return {\n 'project-management': {\n id: 'project-management',\n name: 'Project Management',\n description:\n 'Tasks, dependencies, issues, file tree, docs, drawings, multi terminal, city visualization, code viewer, markdown slides, and excalidraw',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'dependencies',\n 'gitIssues',\n 'fileTree',\n 'docs',\n 'drawings',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'codeViewer',\n 'markdownViewer',\n 'excalidrawDiagram',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'code-review': {\n id: 'code-review',\n name: 'Code Review',\n description:\n 'Git changes, pull requests, and file tree on left, git diff and code viewer in middle, city map on right',\n layout: {\n left: {\n type: 'tabs',\n panels: ['gitChanges', 'gitPullRequests', 'fileTree'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['gitDiff', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: 'cityVisualization',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n documentation: {\n id: 'documentation',\n name: 'Documentation',\n description: 'Docs, markdown viewer, and code viewer',\n layout: {\n left: 'docs',\n middle: 'markdownViewer',\n right: 'codeViewer',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'agent-work': {\n id: 'agent-work',\n name: 'Agent Work',\n description:\n 'Tasks, agent sessions, file tree, agent context, git changes, docs, multi terminal, city map, agent events, code viewer, and markdown slides',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'agentSessions',\n 'fileTree',\n 'agentContext',\n 'gitChanges',\n 'docs',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'agentEvents',\n 'codeViewer',\n 'markdownViewer',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'quality-check': {\n id: 'quality-check',\n name: 'Quality Check',\n description:\n 'Package information, tools, and dependencies on left; city visualization map in middle; multi terminal and code viewer on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['packageInfo', 'tools', 'dependencies'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'cityVisualization',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n drawing: {\n id: 'drawing',\n name: 'Drawing',\n description:\n 'Drawings and docs, excalidraw diagram, multi terminal and markdown viewer',\n layout: {\n left: {\n type: 'tabs',\n panels: ['drawings', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'excalidrawDiagram',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'old-school': {\n id: 'old-school',\n name: 'Old School',\n description:\n 'File tree, search, git changes, and docs on left; code viewer and markdown viewer in middle; multi terminal and city map on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['fileTree', 'search', 'gitChanges', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['codeViewer', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'cityVisualization'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'principal-office': {\n id: 'principal-office',\n name: 'Principal Office',\n description:\n 'Alexandria docs on left, MDX editor in middle, multi terminal on right',\n layout: {\n left: 'docs',\n middle: 'mdxEditor',\n right: 'multiTerminal',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n };\n }\n\n /**\n * Initialize workspace layouts with built-in defaults if none exist\n */\n static async initializeWorkspaceLayouts(): Promise<void> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Only initialize if there are no user workspaces yet\n // Built-in workspaces are always available via getBuiltInWorkspaceLayouts()\n if (Object.keys(userWorkspaces).length === 0) {\n // Nothing to do - built-in workspaces are always available\n // This method exists for compatibility with electron-app\n }\n }\n}\n","/**\n * useWorkspace - React hook for workspace management\n * Provides easy access to workspace operations and state\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { WorkspaceLayoutService } from '../services/WorkspaceLayoutService';\nimport type {\n WorkspaceLayout,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\n\nexport interface UseWorkspaceOptions {\n /**\n * Optional repository key for repository-specific state\n * If provided, the hook will manage repository state\n */\n repositoryKey?: string;\n\n /**\n * Auto-initialize repository state if it doesn't exist\n */\n autoInitialize?: boolean;\n\n /**\n * Default workspace to use for auto-initialization\n */\n defaultWorkspaceId?: string;\n}\n\nexport interface UseWorkspaceReturn {\n /**\n * All available workspaces (built-in + user-created)\n */\n workspaces: Record<string, WorkspaceLayout>;\n\n /**\n * Current repository state (if repositoryKey provided)\n */\n repositoryState: RepositoryWorkspaceState | null;\n\n /**\n * Current active workspace (if repositoryKey provided)\n */\n activeWorkspace: WorkspaceLayout | null;\n\n /**\n * Loading state\n */\n loading: boolean;\n\n /**\n * Error state\n */\n error: Error | null;\n\n /**\n * Get a specific workspace by ID\n */\n getWorkspace: (id: string) => Promise<WorkspaceLayout | null>;\n\n /**\n * Create a new workspace\n */\n createWorkspace: (options: CreateWorkspaceOptions) => Promise<WorkspaceLayout>;\n\n /**\n * Update an existing workspace\n */\n updateWorkspace: (\n id: string,\n updates: UpdateWorkspaceOptions,\n ) => Promise<WorkspaceLayout | null>;\n\n /**\n * Delete a workspace\n */\n deleteWorkspace: (id: string) => Promise<boolean>;\n\n /**\n * Apply a workspace to the current repository\n */\n applyWorkspace: (workspaceId: string) => Promise<void>;\n\n /**\n * Update repository sizes\n */\n updateSizes: (sizes: {\n left: number;\n middle: number;\n right: number;\n }) => Promise<void>;\n\n /**\n * Update repository collapsed state\n */\n updateCollapsed: (collapsed: {\n left?: boolean;\n right?: boolean;\n }) => Promise<void>;\n\n /**\n * Reset repository to workspace defaults\n */\n resetToDefaults: () => Promise<void>;\n\n /**\n * Refresh workspace data\n */\n refresh: () => Promise<void>;\n}\n\nexport function useWorkspace(\n options: UseWorkspaceOptions = {},\n): UseWorkspaceReturn {\n const { repositoryKey, autoInitialize = false, defaultWorkspaceId } = options;\n\n const [workspaces, setWorkspaces] = useState<Record<string, WorkspaceLayout>>(\n {},\n );\n const [repositoryState, setRepositoryState] =\n useState<RepositoryWorkspaceState | null>(null);\n const [activeWorkspace, setActiveWorkspace] =\n useState<WorkspaceLayout | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n /**\n * Load all workspaces and repository state\n */\n const loadData = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n\n // Load all workspaces\n const allWorkspaces =\n await WorkspaceLayoutService.getWorkspaceLayouts();\n setWorkspaces(allWorkspaces);\n\n // Load repository state if repositoryKey provided\n if (repositoryKey) {\n let repoState =\n await WorkspaceLayoutService.getRepositoryState(repositoryKey);\n\n // Auto-initialize if requested and state doesn't exist\n if (!repoState && autoInitialize) {\n const workspaceId =\n defaultWorkspaceId || 'project-management'; // Default to project-management\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n\n if (workspace) {\n repoState = {\n workspaceId,\n sizes: workspace.defaultSizes || {\n left: 20,\n middle: 45,\n right: 35,\n },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(\n repositoryKey,\n repoState,\n );\n }\n }\n\n setRepositoryState(repoState);\n\n // Load active workspace if repository has one\n if (repoState?.workspaceId) {\n const workspace = await WorkspaceLayoutService.getWorkspaceLayout(\n repoState.workspaceId,\n );\n setActiveWorkspace(workspace);\n } else {\n setActiveWorkspace(null);\n }\n }\n } catch (err) {\n setError(err as Error);\n console.error('Failed to load workspace data:', err);\n } finally {\n setLoading(false);\n }\n }, [repositoryKey, autoInitialize, defaultWorkspaceId]);\n\n /**\n * Initial load\n */\n useEffect(() => {\n loadData();\n }, [loadData]);\n\n /**\n * Get a specific workspace\n */\n const getWorkspace = useCallback(\n async (id: string) => {\n return await WorkspaceLayoutService.getWorkspaceLayout(id);\n },\n [],\n );\n\n /**\n * Create a new workspace\n */\n const createWorkspace = useCallback(\n async (createOptions: CreateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.createWorkspaceLayout(createOptions);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Update an existing workspace\n */\n const updateWorkspace = useCallback(\n async (id: string, updates: UpdateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.updateWorkspaceLayout(id, updates);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Delete a workspace\n */\n const deleteWorkspace = useCallback(\n async (id: string) => {\n const success = await WorkspaceLayoutService.deleteWorkspaceLayout(id);\n if (success) {\n await loadData(); // Refresh\n }\n return success;\n },\n [loadData],\n );\n\n /**\n * Apply a workspace to the current repository\n */\n const applyWorkspace = useCallback(\n async (workspaceId: string) => {\n if (!repositoryKey) {\n console.error('Cannot apply workspace without repositoryKey');\n return;\n }\n\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const newState: RepositoryWorkspaceState = {\n workspaceId,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(repositoryKey, newState);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository sizes\n */\n const updateSizes = useCallback(\n async (sizes: { left: number; middle: number; right: number }) => {\n if (!repositoryKey) {\n console.error('Cannot update sizes without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositorySizes(repositoryKey, sizes);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository collapsed state\n */\n const updateCollapsed = useCallback(\n async (collapsed: { left?: boolean; right?: boolean }) => {\n if (!repositoryKey) {\n console.error('Cannot update collapsed without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositoryCollapsed(\n repositoryKey,\n collapsed,\n );\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Reset repository to workspace defaults\n */\n const resetToDefaults = useCallback(async () => {\n if (!repositoryKey || !repositoryState?.workspaceId) {\n console.error('Cannot reset without repositoryKey and active workspace');\n return;\n }\n\n await WorkspaceLayoutService.resetRepositoryToWorkspaceDefaults(\n repositoryKey,\n repositoryState.workspaceId,\n );\n await loadData(); // Refresh\n }, [repositoryKey, repositoryState?.workspaceId, loadData]);\n\n /**\n * Refresh workspace data\n */\n const refresh = useCallback(async () => {\n await loadData();\n }, [loadData]);\n\n return {\n workspaces,\n repositoryState,\n activeWorkspace,\n loading,\n error,\n getWorkspace,\n createWorkspace,\n updateWorkspace,\n deleteWorkspace,\n applyWorkspace,\n updateSizes,\n updateCollapsed,\n resetToDefaults,\n refresh,\n };\n}\n"],"names":["LocalStoragePersistenceAdapter","__publicField","viewKey","stored","error","state","usePanelPersistence","options","defaultSizes","panelType","adapter","sizes","setSizes","useState","collapsed","setCollapsed","prevCollapsedRef","useRef","lastNonZeroSizesRef","saveTimeoutRef","pendingPersistSizesRef","useEffect","saved","updateLastNonZeroSizes","useCallback","incomingSizes","getFallbackSize","panel","storedSize","prevDefaultSizesRef","leftChanged","rightChanged","savePreferences","newSizes","handlePanelResize","sanitizedSizes","shouldPersist","leftCollapsed","leftSize","fallback","rightCollapsed","rightSize","pendingSizes","handleLeftCollapseComplete","handleLeftExpandComplete","handleRightCollapseComplete","handleRightExpandComplete","LocalStorageWorkspaceAdapter","presets","repositoryKey","key","allStates","i","parseError","WorkspaceLayoutService","builtInWorkspaces","id","userWorkspaces","workspace","updatedPresets","updates","existingWorkspace","updatedWorkspace","currentState","repoState","hasSizeDeviation","hasCollapsedDeviation","workspaceId","layout","workspaces","layout1","layout2","name","now","useWorkspace","autoInitialize","defaultWorkspaceId","setWorkspaces","repositoryState","setRepositoryState","activeWorkspace","setActiveWorkspace","loading","setLoading","setError","loadData","allWorkspaces","err","getWorkspace","createWorkspace","createOptions","updateWorkspace","deleteWorkspace","success","applyWorkspace","newState","updateSizes","updateCollapsed","resetToDefaults","refresh"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/hooks/usePanelPersistence.ts","../src/services/LocalStorageWorkspaceAdapter.ts","../src/services/WorkspaceLayoutService.ts","../src/hooks/useWorkspace.ts"],"sourcesContent":["/**\n * usePanelPersistence Hook\n *\n * Extracted and adapted from electron-app/src/renderer/hooks/usePanelPersistence.ts\n * Manages saving and loading panel sizes and collapsed states with debounced persistence.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type {\n UsePanelPersistenceOptions,\n PanelSizes,\n TwoPanelSizes,\n PanelCollapsed,\n PanelPersistence,\n PersistenceAdapter,\n} from '../types/persistence.types';\n\n/**\n * Default localStorage-based persistence adapter for web applications\n */\nexport class LocalStoragePersistenceAdapter implements PersistenceAdapter {\n private storageKey = 'panel-layouts';\n\n async load(viewKey: string) {\n try {\n const stored = localStorage.getItem(`${this.storageKey}:${viewKey}`);\n return stored ? JSON.parse(stored) : null;\n } catch (error) {\n console.error(`Failed to load panel layout for ${viewKey}:`, error);\n return null;\n }\n }\n\n async save(viewKey: string, state: { sizes: PanelSizes | TwoPanelSizes }) {\n try {\n localStorage.setItem(\n `${this.storageKey}:${viewKey}`,\n JSON.stringify(state),\n );\n } catch (error) {\n console.error(`Failed to save panel layout for ${viewKey}:`, error);\n }\n }\n}\n\n/**\n * Hook for persisting panel layouts across sessions\n *\n * Manages panel sizes and collapsed states with automatic debounced saving.\n * Supports both 2-panel and 3-panel layouts.\n *\n * @param options - Configuration options for persistence\n * @returns Panel state and handlers for resizing and collapsing\n *\n * @example\n * ```tsx\n * const persistence = usePanelPersistence({\n * viewKey: 'my-app',\n * defaultSizes: { left: 30, middle: 70, right: 0 },\n * collapsed: { left: false, right: true },\n * panelType: 'three-panel',\n * });\n *\n * <ConfigurablePanelLayout\n * {...otherProps}\n * defaultSizes={persistence.sizes}\n * collapsed={persistence.collapsed}\n * onPanelResize={persistence.handlePanelResize}\n * />\n * ```\n */\nexport function usePanelPersistence(\n options: UsePanelPersistenceOptions,\n): PanelPersistence {\n const {\n viewKey,\n defaultSizes,\n panelType,\n adapter = new LocalStoragePersistenceAdapter(),\n } = options;\n\n const [sizes, setSizes] = useState(defaultSizes);\n const [collapsed, setCollapsed] = useState(options.collapsed);\n const prevCollapsedRef = useRef(options.collapsed);\n const lastNonZeroSizesRef = useRef<Partial<PanelSizes & TwoPanelSizes>>({});\n const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingPersistSizesRef = useRef<typeof sizes | null>(null);\n\n // Load saved state on mount\n useEffect(() => {\n const loadSavedState = async () => {\n const saved = await adapter.load(viewKey);\n if (saved?.sizes) {\n setSizes(saved.sizes as typeof sizes);\n updateLastNonZeroSizes(saved.sizes);\n }\n };\n loadSavedState();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [viewKey]);\n\n const updateLastNonZeroSizes = useCallback(\n (incomingSizes: PanelSizes | TwoPanelSizes) => {\n if ('left' in incomingSizes && incomingSizes.left > 0) {\n lastNonZeroSizesRef.current.left = incomingSizes.left;\n }\n\n if ('middle' in incomingSizes && incomingSizes.middle > 0) {\n lastNonZeroSizesRef.current.middle = incomingSizes.middle;\n }\n\n if ('right' in incomingSizes && incomingSizes.right > 0) {\n lastNonZeroSizesRef.current.right = incomingSizes.right;\n }\n },\n [],\n );\n\n const getFallbackSize = useCallback(\n (panel: 'left' | 'right') => {\n const storedSize = lastNonZeroSizesRef.current[panel];\n if (storedSize && storedSize > 0) {\n return storedSize;\n }\n\n if (panel === 'left' && 'left' in defaultSizes && defaultSizes.left > 0) {\n return defaultSizes.left;\n }\n\n if (\n panel === 'right' &&\n 'right' in defaultSizes &&\n defaultSizes.right > 0\n ) {\n return defaultSizes.right;\n }\n\n return undefined;\n },\n [defaultSizes],\n );\n\n // Update sizes when defaultSizes changes (parent has loaded preferences)\n const prevDefaultSizesRef = useRef(defaultSizes);\n useEffect(() => {\n // Only update if the actual values changed, not just the reference\n const hasChanged =\n ('left' in defaultSizes &&\n defaultSizes.left !== prevDefaultSizesRef.current.left) ||\n ('middle' in defaultSizes &&\n 'middle' in prevDefaultSizesRef.current &&\n defaultSizes.middle !==\n (prevDefaultSizesRef.current as PanelSizes).middle) ||\n ('right' in defaultSizes &&\n defaultSizes.right !== prevDefaultSizesRef.current.right);\n\n if (hasChanged) {\n setSizes(defaultSizes);\n updateLastNonZeroSizes(defaultSizes);\n prevDefaultSizesRef.current = defaultSizes;\n }\n }, [defaultSizes, updateLastNonZeroSizes]);\n\n // Sync with parent's collapsed state (e.g., from titlebar buttons)\n useEffect(() => {\n const leftChanged =\n options.collapsed.left !== prevCollapsedRef.current.left;\n const rightChanged =\n 'right' in options.collapsed &&\n 'right' in prevCollapsedRef.current &&\n (options.collapsed as PanelCollapsed).right !==\n (prevCollapsedRef.current as PanelCollapsed).right;\n\n if (leftChanged || rightChanged) {\n setCollapsed(options.collapsed);\n prevCollapsedRef.current = { ...options.collapsed };\n }\n }, [options.collapsed.left, (options.collapsed as PanelCollapsed).right]);\n\n // Save preferences helper (only saves sizes, not collapsed state)\n const savePreferences = useCallback(\n async (newSizes: typeof sizes) => {\n try {\n await adapter.save(viewKey, { sizes: newSizes });\n } catch (error) {\n console.error(\n `Failed to save panel preferences for ${viewKey}:`,\n error,\n );\n }\n },\n [viewKey, adapter],\n );\n\n // Handle panel resize (debounced)\n const handlePanelResize = useCallback(\n (newSizes: typeof sizes) => {\n const sanitizedSizes = { ...newSizes } as typeof newSizes;\n let shouldPersist = true;\n\n if ('left' in newSizes) {\n const leftCollapsed = Boolean((collapsed as PanelCollapsed)?.left);\n const leftSize = newSizes.left;\n\n if (leftCollapsed) {\n const fallback = getFallbackSize('left');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.left = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (leftSize === 0) {\n shouldPersist = false;\n }\n }\n\n if (panelType === 'three-panel' && 'right' in newSizes) {\n const rightCollapsed = Boolean((collapsed as PanelCollapsed)?.right);\n const rightSize = newSizes.right;\n\n if (rightCollapsed) {\n const fallback = getFallbackSize('right');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.right = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (rightSize === 0) {\n shouldPersist = false;\n }\n }\n\n setSizes(sanitizedSizes);\n updateLastNonZeroSizes(sanitizedSizes);\n\n if (saveTimeoutRef.current) {\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n\n if (!shouldPersist) {\n pendingPersistSizesRef.current = null;\n return;\n }\n\n pendingPersistSizesRef.current = sanitizedSizes;\n saveTimeoutRef.current = setTimeout(() => {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n saveTimeoutRef.current = null;\n }, 500);\n },\n [\n collapsed,\n getFallbackSize,\n panelType,\n savePreferences,\n updateLastNonZeroSizes,\n ],\n );\n\n // Cleanup: save any pending changes on unmount\n useEffect(() => {\n return () => {\n if (saveTimeoutRef.current) {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n };\n }, [savePreferences]);\n\n // Collapse/expand handlers - no-ops because state is controlled by parent\n // The parent manages collapsed state via titlebar buttons and passes it down as props\n const handleLeftCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleLeftExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n if (panelType === 'three-panel') {\n return {\n type: 'three-panel',\n sizes: sizes as PanelSizes,\n collapsed: collapsed as PanelCollapsed,\n handlePanelResize: handlePanelResize as (sizes: PanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n handleRightCollapseComplete,\n handleRightExpandComplete,\n };\n } else {\n return {\n type: 'two-panel',\n sizes: sizes as TwoPanelSizes,\n collapsed: collapsed as { left?: boolean },\n handlePanelResize: handlePanelResize as (sizes: TwoPanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n };\n }\n}\n","/**\n * LocalStorageWorkspaceAdapter - Browser localStorage implementation\n * Stores workspace presets and repository states in browser localStorage\n */\n\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n} from '../types/workspace.types';\n\nexport class LocalStorageWorkspaceAdapter implements WorkspaceStorageAdapter {\n private readonly PRESETS_KEY = 'panel-layouts:workspace-presets';\n private readonly REPO_STATE_PREFIX = 'panel-layouts:repo-state:';\n\n /**\n * Load all user-created workspace presets\n */\n async loadWorkspacePresets(): Promise<Record<string, WorkspaceLayout>> {\n try {\n const stored = localStorage.getItem(this.PRESETS_KEY);\n if (!stored) {\n return {};\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error('Failed to load workspace presets:', error);\n return {};\n }\n }\n\n /**\n * Save workspace presets\n */\n async saveWorkspacePresets(\n presets: Record<string, WorkspaceLayout>,\n ): Promise<void> {\n try {\n localStorage.setItem(this.PRESETS_KEY, JSON.stringify(presets));\n } catch (error) {\n console.error('Failed to save workspace presets:', error);\n throw error;\n }\n }\n\n /**\n * Load repository-specific workspace state\n */\n async loadRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n const stored = localStorage.getItem(key);\n if (!stored) {\n return null;\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error(\n `Failed to load repository state for ${repositoryKey}:`,\n error,\n );\n return null;\n }\n }\n\n /**\n * Save repository-specific workspace state\n */\n async saveRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n localStorage.setItem(key, JSON.stringify(state));\n } catch (error) {\n console.error(\n `Failed to save repository state for ${repositoryKey}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Load all repository states\n */\n async loadAllRepositoryStates(): Promise<\n Record<string, RepositoryWorkspaceState>\n > {\n try {\n const allStates: Record<string, RepositoryWorkspaceState> = {};\n\n // Iterate through all localStorage keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(this.REPO_STATE_PREFIX)) {\n const repositoryKey = key.substring(this.REPO_STATE_PREFIX.length);\n const stored = localStorage.getItem(key);\n if (stored) {\n try {\n allStates[repositoryKey] = JSON.parse(stored);\n } catch (parseError) {\n console.error(\n `Failed to parse repository state for ${repositoryKey}:`,\n parseError,\n );\n }\n }\n }\n }\n\n return allStates;\n } catch (error) {\n console.error('Failed to load all repository states:', error);\n return {};\n }\n }\n}\n","/**\n * WorkspaceLayoutService - Manages workspace layout presets\n * Extracted from electron-app/src/renderer/services/WorkspaceLayoutService.ts\n *\n * This service handles CRUD operations for workspace layouts (panel configuration presets)\n * and provides utilities for comparing layouts and managing built-in workspaces.\n */\n\nimport type { PanelLayout } from '@principal-ade/panels';\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\nimport { LocalStorageWorkspaceAdapter } from './LocalStorageWorkspaceAdapter';\n\nexport class WorkspaceLayoutService {\n private static adapter: WorkspaceStorageAdapter =\n new LocalStorageWorkspaceAdapter();\n\n /**\n * Configure a custom storage adapter (for Electron IPC or remote storage)\n */\n static setAdapter(adapter: WorkspaceStorageAdapter): void {\n this.adapter = adapter;\n }\n\n /**\n * Get all workspace layouts (including built-in)\n */\n static async getWorkspaceLayouts(): Promise<Record<string, WorkspaceLayout>> {\n const builtInWorkspaces = this.getBuiltInWorkspaceLayouts();\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Merge built-in and user workspaces, built-in takes precedence\n return {\n ...userWorkspaces,\n ...builtInWorkspaces,\n };\n }\n\n /**\n * Get a specific workspace layout by ID\n */\n static async getWorkspaceLayout(id: string): Promise<WorkspaceLayout | null> {\n const workspaces = await this.getWorkspaceLayouts();\n return workspaces[id] || null;\n }\n\n /**\n * Create a new workspace layout\n */\n static async createWorkspaceLayout(\n options: CreateWorkspaceOptions,\n ): Promise<WorkspaceLayout> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const id = this.generateWorkspaceId(options.name);\n\n const workspace: WorkspaceLayout = {\n id,\n name: options.name,\n description: options.description,\n layout: options.layout,\n defaultSizes: options.defaultSizes,\n defaultCollapsed: options.defaultCollapsed,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n isBuiltIn: false,\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: workspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return workspace;\n }\n\n /**\n * Update an existing workspace layout\n */\n static async updateWorkspaceLayout(\n id: string,\n updates: UpdateWorkspaceOptions,\n ): Promise<WorkspaceLayout | null> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const existingWorkspace = userWorkspaces[id];\n\n if (!existingWorkspace) {\n console.error(`Workspace layout ${id} not found`);\n return null;\n }\n\n // Don't allow updating built-in workspaces\n if (existingWorkspace.isBuiltIn) {\n console.error(`Cannot update built-in workspace layout ${id}`);\n return null;\n }\n\n const updatedWorkspace: WorkspaceLayout = {\n ...existingWorkspace,\n ...updates,\n id, // Preserve ID\n createdAt: existingWorkspace.createdAt, // Preserve creation time\n updatedAt: Date.now(),\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: updatedWorkspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return updatedWorkspace;\n }\n\n /**\n * Delete a workspace layout\n */\n static async deleteWorkspaceLayout(id: string): Promise<boolean> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const workspace = userWorkspaces[id];\n\n if (!workspace) {\n console.error(`Workspace layout ${id} not found`);\n return false;\n }\n\n // Don't allow deleting built-in workspaces\n if (workspace.isBuiltIn) {\n console.error(`Cannot delete built-in workspace layout ${id}`);\n return false;\n }\n\n const updatedPresets = { ...userWorkspaces };\n delete updatedPresets[id];\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return true;\n }\n\n /**\n * Get repository state (which workspace + current sizes/collapsed)\n */\n static async getRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n return await this.adapter.loadRepositoryState(repositoryKey);\n }\n\n /**\n * Set repository state (which workspace + current sizes/collapsed/layout)\n */\n static async setRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n await this.adapter.saveRepositoryState(repositoryKey, state);\n }\n\n /**\n * Update only sizes in repository state\n */\n static async updateRepositorySizes(\n repositoryKey: string,\n sizes: { left: number; middle: number; right: number },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes,\n });\n }\n\n /**\n * Update only collapsed state in repository state\n */\n static async updateRepositoryCollapsed(\n repositoryKey: string,\n collapsed: { left?: boolean; right?: boolean },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n collapsed,\n });\n }\n\n /**\n * Check if repository state differs from workspace defaults\n */\n static hasStateDeviation(\n repoState: {\n workspaceId: string | null;\n sizes: { left: number; middle: number; right: number };\n collapsed: { left?: boolean; right?: boolean };\n },\n workspace: WorkspaceLayout,\n ): {\n hasSizeDeviation: boolean;\n hasCollapsedDeviation: boolean;\n } {\n const hasSizeDeviation = workspace.defaultSizes\n ? JSON.stringify(repoState.sizes) !==\n JSON.stringify(workspace.defaultSizes)\n : false;\n\n const hasCollapsedDeviation = workspace.defaultCollapsed\n ? JSON.stringify(repoState.collapsed) !==\n JSON.stringify(workspace.defaultCollapsed)\n : false;\n\n return { hasSizeDeviation, hasCollapsedDeviation };\n }\n\n /**\n * Update workspace defaults from repository state\n */\n static async updateWorkspaceFromRepositoryState(\n workspaceId: string,\n repositoryKey: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n const repoState = await this.getRepositoryState(repositoryKey);\n\n if (!workspace || !repoState || workspace.isBuiltIn) {\n console.error(\n 'Cannot update built-in workspace or workspace/state not found',\n );\n return;\n }\n\n await this.updateWorkspaceLayout(workspaceId, {\n defaultSizes: repoState.sizes,\n defaultCollapsed: repoState.collapsed,\n });\n }\n\n /**\n * Reset repository state to workspace defaults\n */\n static async resetRepositoryToWorkspaceDefaults(\n repositoryKey: string,\n workspaceId: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || { left: false, right: false },\n });\n }\n\n /**\n * Check if a layout matches a workspace layout\n */\n static isLayoutMatchingWorkspace(\n layout: PanelLayout,\n workspace: WorkspaceLayout,\n ): boolean {\n return this.areLayoutsEqual(layout, workspace.layout);\n }\n\n /**\n * Find workspace ID that matches the given layout\n */\n static async findMatchingWorkspace(\n layout: PanelLayout,\n ): Promise<string | null> {\n const workspaces = await this.getWorkspaceLayouts();\n\n for (const [id, workspace] of Object.entries(workspaces)) {\n if (this.isLayoutMatchingWorkspace(layout, workspace)) {\n return id;\n }\n }\n\n return null;\n }\n\n /**\n * Deep comparison of two panel layouts\n */\n private static areLayoutsEqual(\n layout1: PanelLayout,\n layout2: PanelLayout,\n ): boolean {\n return JSON.stringify(layout1) === JSON.stringify(layout2);\n }\n\n /**\n * Generate a unique ID from a workspace name\n */\n private static generateWorkspaceId(name: string): string {\n const base = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n return `${base}-${Date.now()}`;\n }\n\n /**\n * Get built-in workspace layouts\n */\n static getBuiltInWorkspaceLayouts(): Record<string, WorkspaceLayout> {\n const now = Date.now();\n\n return {\n 'project-management': {\n id: 'project-management',\n name: 'Project Management',\n description:\n 'Tasks, dependencies, issues, file tree, docs, drawings, multi terminal, city visualization, code viewer, markdown slides, and excalidraw',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'dependencies',\n 'gitIssues',\n 'fileTree',\n 'docs',\n 'drawings',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'codeViewer',\n 'markdownViewer',\n 'excalidrawDiagram',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'code-review': {\n id: 'code-review',\n name: 'Code Review',\n description:\n 'Git changes, pull requests, and file tree on left, git diff and code viewer in middle, city map on right',\n layout: {\n left: {\n type: 'tabs',\n panels: ['gitChanges', 'gitPullRequests', 'fileTree'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['gitDiff', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: 'cityVisualization',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n documentation: {\n id: 'documentation',\n name: 'Documentation',\n description: 'Docs, markdown viewer, and code viewer',\n layout: {\n left: 'docs',\n middle: 'markdownViewer',\n right: 'codeViewer',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'agent-work': {\n id: 'agent-work',\n name: 'Agent Work',\n description:\n 'Tasks, agent sessions, file tree, agent context, git changes, docs, multi terminal, city map, agent events, code viewer, and markdown slides',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'agentSessions',\n 'fileTree',\n 'agentContext',\n 'gitChanges',\n 'docs',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'agentEvents',\n 'codeViewer',\n 'markdownViewer',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'quality-check': {\n id: 'quality-check',\n name: 'Quality Check',\n description:\n 'Package information, tools, and dependencies on left; city visualization map in middle; multi terminal and code viewer on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['packageInfo', 'tools', 'dependencies'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'cityVisualization',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n drawing: {\n id: 'drawing',\n name: 'Drawing',\n description:\n 'Drawings and docs, excalidraw diagram, multi terminal and markdown viewer',\n layout: {\n left: {\n type: 'tabs',\n panels: ['drawings', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'excalidrawDiagram',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'old-school': {\n id: 'old-school',\n name: 'Old School',\n description:\n 'File tree, search, git changes, and docs on left; code viewer and markdown viewer in middle; multi terminal and city map on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['fileTree', 'search', 'gitChanges', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['codeViewer', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'cityVisualization'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'principal-office': {\n id: 'principal-office',\n name: 'Principal Office',\n description:\n 'Alexandria docs on left, MDX editor in middle, multi terminal on right',\n layout: {\n left: 'docs',\n middle: 'mdxEditor',\n right: 'multiTerminal',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n };\n }\n\n /**\n * Initialize workspace layouts with built-in defaults if none exist\n */\n static async initializeWorkspaceLayouts(): Promise<void> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Only initialize if there are no user workspaces yet\n // Built-in workspaces are always available via getBuiltInWorkspaceLayouts()\n if (Object.keys(userWorkspaces).length === 0) {\n // Nothing to do - built-in workspaces are always available\n // This method exists for compatibility with electron-app\n }\n }\n}\n","/**\n * useWorkspace - React hook for workspace management\n * Provides easy access to workspace operations and state\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { WorkspaceLayoutService } from '../services/WorkspaceLayoutService';\nimport type {\n WorkspaceLayout,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\n\nexport interface UseWorkspaceOptions {\n /**\n * Optional repository key for repository-specific state\n * If provided, the hook will manage repository state\n */\n repositoryKey?: string;\n\n /**\n * Auto-initialize repository state if it doesn't exist\n */\n autoInitialize?: boolean;\n\n /**\n * Default workspace to use for auto-initialization\n */\n defaultWorkspaceId?: string;\n}\n\nexport interface UseWorkspaceReturn {\n /**\n * All available workspaces (built-in + user-created)\n */\n workspaces: Record<string, WorkspaceLayout>;\n\n /**\n * Current repository state (if repositoryKey provided)\n */\n repositoryState: RepositoryWorkspaceState | null;\n\n /**\n * Current active workspace (if repositoryKey provided)\n */\n activeWorkspace: WorkspaceLayout | null;\n\n /**\n * Loading state\n */\n loading: boolean;\n\n /**\n * Error state\n */\n error: Error | null;\n\n /**\n * Get a specific workspace by ID\n */\n getWorkspace: (id: string) => Promise<WorkspaceLayout | null>;\n\n /**\n * Create a new workspace\n */\n createWorkspace: (options: CreateWorkspaceOptions) => Promise<WorkspaceLayout>;\n\n /**\n * Update an existing workspace\n */\n updateWorkspace: (\n id: string,\n updates: UpdateWorkspaceOptions,\n ) => Promise<WorkspaceLayout | null>;\n\n /**\n * Delete a workspace\n */\n deleteWorkspace: (id: string) => Promise<boolean>;\n\n /**\n * Apply a workspace to the current repository\n */\n applyWorkspace: (workspaceId: string) => Promise<void>;\n\n /**\n * Update repository sizes\n */\n updateSizes: (sizes: {\n left: number;\n middle: number;\n right: number;\n }) => Promise<void>;\n\n /**\n * Update repository collapsed state\n */\n updateCollapsed: (collapsed: {\n left?: boolean;\n right?: boolean;\n }) => Promise<void>;\n\n /**\n * Reset repository to workspace defaults\n */\n resetToDefaults: () => Promise<void>;\n\n /**\n * Refresh workspace data\n */\n refresh: () => Promise<void>;\n}\n\nexport function useWorkspace(\n options: UseWorkspaceOptions = {},\n): UseWorkspaceReturn {\n const { repositoryKey, autoInitialize = false, defaultWorkspaceId } = options;\n\n const [workspaces, setWorkspaces] = useState<Record<string, WorkspaceLayout>>(\n {},\n );\n const [repositoryState, setRepositoryState] =\n useState<RepositoryWorkspaceState | null>(null);\n const [activeWorkspace, setActiveWorkspace] =\n useState<WorkspaceLayout | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n /**\n * Load all workspaces and repository state\n */\n const loadData = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n\n // Load all workspaces\n const allWorkspaces =\n await WorkspaceLayoutService.getWorkspaceLayouts();\n setWorkspaces(allWorkspaces);\n\n // Load repository state if repositoryKey provided\n if (repositoryKey) {\n let repoState =\n await WorkspaceLayoutService.getRepositoryState(repositoryKey);\n\n // Auto-initialize if requested and state doesn't exist\n if (!repoState && autoInitialize) {\n const workspaceId =\n defaultWorkspaceId || 'project-management'; // Default to project-management\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n\n if (workspace) {\n repoState = {\n workspaceId,\n sizes: workspace.defaultSizes || {\n left: 20,\n middle: 45,\n right: 35,\n },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(\n repositoryKey,\n repoState,\n );\n }\n }\n\n setRepositoryState(repoState);\n\n // Load active workspace if repository has one\n if (repoState?.workspaceId) {\n const workspace = await WorkspaceLayoutService.getWorkspaceLayout(\n repoState.workspaceId,\n );\n setActiveWorkspace(workspace);\n } else {\n setActiveWorkspace(null);\n }\n }\n } catch (err) {\n setError(err as Error);\n console.error('Failed to load workspace data:', err);\n } finally {\n setLoading(false);\n }\n }, [repositoryKey, autoInitialize, defaultWorkspaceId]);\n\n /**\n * Initial load\n */\n useEffect(() => {\n loadData();\n }, [loadData]);\n\n /**\n * Get a specific workspace\n */\n const getWorkspace = useCallback(\n async (id: string) => {\n return await WorkspaceLayoutService.getWorkspaceLayout(id);\n },\n [],\n );\n\n /**\n * Create a new workspace\n */\n const createWorkspace = useCallback(\n async (createOptions: CreateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.createWorkspaceLayout(createOptions);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Update an existing workspace\n */\n const updateWorkspace = useCallback(\n async (id: string, updates: UpdateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.updateWorkspaceLayout(id, updates);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Delete a workspace\n */\n const deleteWorkspace = useCallback(\n async (id: string) => {\n const success = await WorkspaceLayoutService.deleteWorkspaceLayout(id);\n if (success) {\n await loadData(); // Refresh\n }\n return success;\n },\n [loadData],\n );\n\n /**\n * Apply a workspace to the current repository\n */\n const applyWorkspace = useCallback(\n async (workspaceId: string) => {\n if (!repositoryKey) {\n console.error('Cannot apply workspace without repositoryKey');\n return;\n }\n\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const newState: RepositoryWorkspaceState = {\n workspaceId,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(repositoryKey, newState);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository sizes\n */\n const updateSizes = useCallback(\n async (sizes: { left: number; middle: number; right: number }) => {\n if (!repositoryKey) {\n console.error('Cannot update sizes without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositorySizes(repositoryKey, sizes);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository collapsed state\n */\n const updateCollapsed = useCallback(\n async (collapsed: { left?: boolean; right?: boolean }) => {\n if (!repositoryKey) {\n console.error('Cannot update collapsed without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositoryCollapsed(\n repositoryKey,\n collapsed,\n );\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Reset repository to workspace defaults\n */\n const resetToDefaults = useCallback(async () => {\n if (!repositoryKey || !repositoryState?.workspaceId) {\n console.error('Cannot reset without repositoryKey and active workspace');\n return;\n }\n\n await WorkspaceLayoutService.resetRepositoryToWorkspaceDefaults(\n repositoryKey,\n repositoryState.workspaceId,\n );\n await loadData(); // Refresh\n }, [repositoryKey, repositoryState?.workspaceId, loadData]);\n\n /**\n * Refresh workspace data\n */\n const refresh = useCallback(async () => {\n await loadData();\n }, [loadData]);\n\n return {\n workspaces,\n repositoryState,\n activeWorkspace,\n loading,\n error,\n getWorkspace,\n createWorkspace,\n updateWorkspace,\n deleteWorkspace,\n applyWorkspace,\n updateSizes,\n updateCollapsed,\n resetToDefaults,\n refresh,\n };\n}\n"],"names":["LocalStoragePersistenceAdapter","__publicField","viewKey","stored","error","state","usePanelPersistence","options","defaultSizes","panelType","adapter","sizes","setSizes","useState","collapsed","setCollapsed","prevCollapsedRef","useRef","lastNonZeroSizesRef","saveTimeoutRef","pendingPersistSizesRef","useEffect","saved","updateLastNonZeroSizes","useCallback","incomingSizes","getFallbackSize","panel","storedSize","prevDefaultSizesRef","leftChanged","rightChanged","savePreferences","newSizes","handlePanelResize","sanitizedSizes","shouldPersist","leftCollapsed","leftSize","fallback","rightCollapsed","rightSize","pendingSizes","handleLeftCollapseComplete","handleLeftExpandComplete","handleRightCollapseComplete","handleRightExpandComplete","LocalStorageWorkspaceAdapter","presets","repositoryKey","key","allStates","i","parseError","WorkspaceLayoutService","builtInWorkspaces","id","userWorkspaces","workspace","updatedPresets","updates","existingWorkspace","updatedWorkspace","currentState","repoState","hasSizeDeviation","hasCollapsedDeviation","workspaceId","layout","workspaces","layout1","layout2","name","now","useWorkspace","autoInitialize","defaultWorkspaceId","setWorkspaces","repositoryState","setRepositoryState","activeWorkspace","setActiveWorkspace","loading","setLoading","setError","loadData","allWorkspaces","err","getWorkspace","createWorkspace","createOptions","updateWorkspace","deleteWorkspace","success","applyWorkspace","newState","updateSizes","updateCollapsed","resetToDefaults","refresh"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";require('./index.css');var $=Object.defineProperty;var D=(c,e,t)=>e in c?$(c,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):c[e]=t;var P=(c,e,t)=>D(c,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const L=require("@a24z/panels"),s=require("react");class I{constructor(){P(this,"storageKey","panel-layouts")}async load(e){try{const t=localStorage.getItem(`${this.storageKey}:${e}`);return t?JSON.parse(t):null}catch(t){return console.error(`Failed to load panel layout for ${e}:`,t),null}}async save(e,t){try{localStorage.setItem(`${this.storageKey}:${e}`,JSON.stringify(t))}catch(a){console.error(`Failed to save panel layout for ${e}:`,a)}}}function V(c){const{viewKey:e,defaultSizes:t,panelType:a,adapter:r=new I}=c,[f,u]=s.useState(t),[y,A]=s.useState(c.collapsed),S=s.useRef(c.collapsed),W=s.useRef({}),h=s.useRef(null),w=s.useRef(null);s.useEffect(()=>{(async()=>{const d=await r.load(e);d!=null&&d.sizes&&(u(d.sizes),m(d.sizes))})()},[e]);const m=s.useCallback(o=>{"left"in o&&o.left>0&&(W.current.left=o.left),"middle"in o&&o.middle>0&&(W.current.middle=o.middle),"right"in o&&o.right>0&&(W.current.right=o.right)},[]),n=s.useCallback(o=>{const d=W.current[o];if(d&&d>0)return d;if(o==="left"&&"left"in t&&t.left>0)return t.left;if(o==="right"&&"right"in t&&t.right>0)return t.right},[t]),b=s.useRef(t);s.useEffect(()=>{("left"in t&&t.left!==b.current.left||"middle"in t&&"middle"in b.current&&t.middle!==b.current.middle||"right"in t&&t.right!==b.current.right)&&(u(t),m(t),b.current=t)},[t,m]),s.useEffect(()=>{const o=c.collapsed.left!==S.current.left,d="right"in c.collapsed&&"right"in S.current&&c.collapsed.right!==S.current.right;(o||d)&&(A(c.collapsed),S.current={...c.collapsed})},[c.collapsed.left,c.collapsed.right]);const C=s.useCallback(async o=>{try{await r.save(e,{sizes:o})}catch(d){console.error(`Failed to save panel preferences for ${e}:`,d)}},[e,r]),T=s.useCallback(o=>{const d={...o};let i=!0;if("left"in o){const l=!!(y!=null&&y.left),g=o.left;if(l){const k=n("left");k!==void 0&&k>0?d.left=k:i=!1}else g===0&&(i=!1)}if(a==="three-panel"&&"right"in o){const l=!!(y!=null&&y.right),g=o.right;if(l){const k=n("right");k!==void 0&&k>0?d.right=k:i=!1}else g===0&&(i=!1)}if(u(d),m(d),h.current&&(clearTimeout(h.current),h.current=null),!i){w.current=null;return}w.current=d,h.current=setTimeout(()=>{const l=w.current;l&&(C(l),w.current=null),h.current=null},500)},[y,n,a,C,m]);s.useEffect(()=>()=>{if(h.current){const o=w.current;o&&(C(o),w.current=null),clearTimeout(h.current),h.current=null}},[C]);const v=s.useCallback(async()=>{},[]),R=s.useCallback(async()=>{},[]),z=s.useCallback(async()=>{},[]),E=s.useCallback(async()=>{},[]);return a==="three-panel"?{type:"three-panel",sizes:f,collapsed:y,handlePanelResize:T,handleLeftCollapseComplete:v,handleLeftExpandComplete:R,handleRightCollapseComplete:z,handleRightExpandComplete:E}:{type:"two-panel",sizes:f,collapsed:y,handlePanelResize:T,handleLeftCollapseComplete:v,handleLeftExpandComplete:R}}class O{constructor(){P(this,"PRESETS_KEY","panel-layouts:workspace-presets");P(this,"REPO_STATE_PREFIX","panel-layouts:repo-state:")}async loadWorkspacePresets(){try{const e=localStorage.getItem(this.PRESETS_KEY);return e?JSON.parse(e):{}}catch(e){return console.error("Failed to load workspace presets:",e),{}}}async saveWorkspacePresets(e){try{localStorage.setItem(this.PRESETS_KEY,JSON.stringify(e))}catch(t){throw console.error("Failed to save workspace presets:",t),t}}async loadRepositoryState(e){try{const t=`${this.REPO_STATE_PREFIX}${e}`,a=localStorage.getItem(t);return a?JSON.parse(a):null}catch(t){return console.error(`Failed to load repository state for ${e}:`,t),null}}async saveRepositoryState(e,t){try{const a=`${this.REPO_STATE_PREFIX}${e}`;localStorage.setItem(a,JSON.stringify(t))}catch(a){throw console.error(`Failed to save repository state for ${e}:`,a),a}}async loadAllRepositoryStates(){try{const e={};for(let t=0;t<localStorage.length;t++){const a=localStorage.key(t);if(a&&a.startsWith(this.REPO_STATE_PREFIX)){const r=a.substring(this.REPO_STATE_PREFIX.length),f=localStorage.getItem(a);if(f)try{e[r]=JSON.parse(f)}catch(u){console.error(`Failed to parse repository state for ${r}:`,u)}}}return e}catch(e){return console.error("Failed to load all repository states:",e),{}}}}class p{static setAdapter(e){this.adapter=e}static async getWorkspaceLayouts(){const e=this.getBuiltInWorkspaceLayouts();return{...await this.adapter.loadWorkspacePresets(),...e}}static async getWorkspaceLayout(e){return(await this.getWorkspaceLayouts())[e]||null}static async createWorkspaceLayout(e){const t=await this.adapter.loadWorkspacePresets(),a=this.generateWorkspaceId(e.name),r={id:a,name:e.name,description:e.description,layout:e.layout,defaultSizes:e.defaultSizes,defaultCollapsed:e.defaultCollapsed,createdAt:Date.now(),updatedAt:Date.now(),isBuiltIn:!1},f={...t,[a]:r};return await this.adapter.saveWorkspacePresets(f),r}static async updateWorkspaceLayout(e,t){const a=await this.adapter.loadWorkspacePresets(),r=a[e];if(!r)return console.error(`Workspace layout ${e} not found`),null;if(r.isBuiltIn)return console.error(`Cannot update built-in workspace layout ${e}`),null;const f={...r,...t,id:e,createdAt:r.createdAt,updatedAt:Date.now()},u={...a,[e]:f};return await this.adapter.saveWorkspacePresets(u),f}static async deleteWorkspaceLayout(e){const t=await this.adapter.loadWorkspacePresets(),a=t[e];if(!a)return console.error(`Workspace layout ${e} not found`),!1;if(a.isBuiltIn)return console.error(`Cannot delete built-in workspace layout ${e}`),!1;const r={...t};return delete r[e],await this.adapter.saveWorkspacePresets(r),!0}static async getRepositoryState(e){return await this.adapter.loadRepositoryState(e)}static async setRepositoryState(e,t){await this.adapter.saveRepositoryState(e,t)}static async updateRepositorySizes(e,t){const a=await this.getRepositoryState(e);a&&await this.setRepositoryState(e,{...a,sizes:t})}static async updateRepositoryCollapsed(e,t){const a=await this.getRepositoryState(e);a&&await this.setRepositoryState(e,{...a,collapsed:t})}static hasStateDeviation(e,t){const a=t.defaultSizes?JSON.stringify(e.sizes)!==JSON.stringify(t.defaultSizes):!1,r=t.defaultCollapsed?JSON.stringify(e.collapsed)!==JSON.stringify(t.defaultCollapsed):!1;return{hasSizeDeviation:a,hasCollapsedDeviation:r}}static async updateWorkspaceFromRepositoryState(e,t){const a=await this.getWorkspaceLayout(e),r=await this.getRepositoryState(t);if(!a||!r||a.isBuiltIn){console.error("Cannot update built-in workspace or workspace/state not found");return}await this.updateWorkspaceLayout(e,{defaultSizes:r.sizes,defaultCollapsed:r.collapsed})}static async resetRepositoryToWorkspaceDefaults(e,t){const a=await this.getWorkspaceLayout(t);if(!a){console.error(`Workspace ${t} not found`);return}const r=await this.getRepositoryState(e);r&&await this.setRepositoryState(e,{...r,sizes:a.defaultSizes||{left:20,middle:45,right:35},collapsed:a.defaultCollapsed||{left:!1,right:!1}})}static isLayoutMatchingWorkspace(e,t){return this.areLayoutsEqual(e,t.layout)}static async findMatchingWorkspace(e){const t=await this.getWorkspaceLayouts();for(const[a,r]of Object.entries(t))if(this.isLayoutMatchingWorkspace(e,r))return a;return null}static areLayoutsEqual(e,t){return JSON.stringify(e)===JSON.stringify(t)}static generateWorkspaceId(e){return`${e.toLowerCase().replace(/[^a-z0-9]+/g,"-")}-${Date.now()}`}static getBuiltInWorkspaceLayouts(){const e=Date.now();return{"project-management":{id:"project-management",name:"Project Management",description:"Tasks, dependencies, issues, file tree, docs, drawings, multi terminal, city visualization, code viewer, markdown slides, and excalidraw",layout:{left:{type:"tabs",panels:["tasks","dependencies","gitIssues","fileTree","docs","drawings"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"multiTerminal",right:{type:"tabs",panels:["cityVisualization","codeViewer","markdownViewer","excalidrawDiagram"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:45,right:35},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0},"code-review":{id:"code-review",name:"Code Review",description:"Git changes, pull requests, and file tree on left, git diff and code viewer in middle, city map on right",layout:{left:{type:"tabs",panels:["gitChanges","gitPullRequests","fileTree"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:{type:"tabs",panels:["gitDiff","codeViewer"],config:{defaultActiveTab:0,tabPosition:"top"}},right:"cityVisualization"},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0},documentation:{id:"documentation",name:"Documentation",description:"Docs, markdown viewer, and code viewer",layout:{left:"docs",middle:"markdownViewer",right:"codeViewer"},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},"agent-work":{id:"agent-work",name:"Agent Work",description:"Tasks, agent sessions, file tree, agent context, git changes, docs, multi terminal, city map, agent events, code viewer, and markdown slides",layout:{left:{type:"tabs",panels:["tasks","agentSessions","fileTree","agentContext","gitChanges","docs"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"multiTerminal",right:{type:"tabs",panels:["cityVisualization","agentEvents","codeViewer","markdownViewer"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:45,right:35},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0},"quality-check":{id:"quality-check",name:"Quality Check",description:"Package information, tools, and dependencies on left; city visualization map in middle; multi terminal and code viewer on right (collapsed)",layout:{left:{type:"tabs",panels:["packageInfo","tools","dependencies"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"cityVisualization",right:{type:"tabs",panels:["multiTerminal","codeViewer"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:45,right:35},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},drawing:{id:"drawing",name:"Drawing",description:"Drawings and docs, excalidraw diagram, multi terminal and markdown viewer",layout:{left:{type:"tabs",panels:["drawings","docs"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"excalidrawDiagram",right:{type:"tabs",panels:["multiTerminal","markdownViewer"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},"old-school":{id:"old-school",name:"Old School",description:"File tree, search, git changes, and docs on left; code viewer and markdown viewer in middle; multi terminal and city map on right (collapsed)",layout:{left:{type:"tabs",panels:["fileTree","search","gitChanges","docs"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:{type:"tabs",panels:["codeViewer","markdownViewer"],config:{defaultActiveTab:0,tabPosition:"top"}},right:{type:"tabs",panels:["multiTerminal","cityVisualization"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},"principal-office":{id:"principal-office",name:"Principal Office",description:"Alexandria docs on left, MDX editor in middle, multi terminal on right",layout:{left:"docs",middle:"mdxEditor",right:"multiTerminal"},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0}}}static async initializeWorkspaceLayouts(){const e=await this.adapter.loadWorkspacePresets();Object.keys(e).length}}P(p,"adapter",new O);function F(c={}){const{repositoryKey:e,autoInitialize:t=!1,defaultWorkspaceId:a}=c,[r,f]=s.useState({}),[u,y]=s.useState(null),[A,S]=s.useState(null),[W,h]=s.useState(!0),[w,m]=s.useState(null),n=s.useCallback(async()=>{try{h(!0),m(null);const i=await p.getWorkspaceLayouts();if(f(i),e){let l=await p.getRepositoryState(e);if(!l&&t){const g=a||"project-management",k=await p.getWorkspaceLayout(g);k&&(l={workspaceId:g,sizes:k.defaultSizes||{left:20,middle:45,right:35},collapsed:k.defaultCollapsed||{left:!1,right:!1}},await p.setRepositoryState(e,l))}if(y(l),l!=null&&l.workspaceId){const g=await p.getWorkspaceLayout(l.workspaceId);S(g)}else S(null)}}catch(i){m(i),console.error("Failed to load workspace data:",i)}finally{h(!1)}},[e,t,a]);s.useEffect(()=>{n()},[n]);const b=s.useCallback(async i=>await p.getWorkspaceLayout(i),[]),C=s.useCallback(async i=>{const l=await p.createWorkspaceLayout(i);return await n(),l},[n]),T=s.useCallback(async(i,l)=>{const g=await p.updateWorkspaceLayout(i,l);return await n(),g},[n]),v=s.useCallback(async i=>{const l=await p.deleteWorkspaceLayout(i);return l&&await n(),l},[n]),R=s.useCallback(async i=>{if(!e){console.error("Cannot apply workspace without repositoryKey");return}const l=await p.getWorkspaceLayout(i);if(!l){console.error(`Workspace ${i} not found`);return}const g={workspaceId:i,sizes:l.defaultSizes||{left:20,middle:45,right:35},collapsed:l.defaultCollapsed||{left:!1,right:!1}};await p.setRepositoryState(e,g),await n()},[e,n]),z=s.useCallback(async i=>{if(!e){console.error("Cannot update sizes without repositoryKey");return}await p.updateRepositorySizes(e,i),await n()},[e,n]),E=s.useCallback(async i=>{if(!e){console.error("Cannot update collapsed without repositoryKey");return}await p.updateRepositoryCollapsed(e,i),await n()},[e,n]),o=s.useCallback(async()=>{if(!e||!(u!=null&&u.workspaceId)){console.error("Cannot reset without repositoryKey and active workspace");return}await p.resetRepositoryToWorkspaceDefaults(e,u.workspaceId),await n()},[e,u==null?void 0:u.workspaceId,n]),d=s.useCallback(async()=>{await n()},[n]);return{workspaces:r,repositoryState:u,activeWorkspace:A,loading:W,error:w,getWorkspace:b,createWorkspace:C,updateWorkspace:T,deleteWorkspace:v,applyWorkspace:R,updateSizes:z,updateCollapsed:E,resetToDefaults:o,refresh:d}}Object.defineProperty(exports,"EditableConfigurablePanelLayout",{enumerable:!0,get:()=>L.EditableConfigurablePanelLayout});Object.defineProperty(exports,"mapThemeToPanelVars",{enumerable:!0,get:()=>L.mapThemeToPanelVars});Object.defineProperty(exports,"mapThemeToTabVars",{enumerable:!0,get:()=>L.mapThemeToTabVars});exports.LocalStoragePersistenceAdapter=I;exports.LocalStorageWorkspaceAdapter=O;exports.WorkspaceLayoutService=p;exports.usePanelPersistence=V;exports.useWorkspace=F;
|
|
1
|
+
"use strict";require('./index.css');var $=Object.defineProperty;var D=(c,e,t)=>e in c?$(c,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):c[e]=t;var P=(c,e,t)=>D(c,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const A=require("@principal-ade/panels"),s=require("react");class I{constructor(){P(this,"storageKey","panel-layouts")}async load(e){try{const t=localStorage.getItem(`${this.storageKey}:${e}`);return t?JSON.parse(t):null}catch(t){return console.error(`Failed to load panel layout for ${e}:`,t),null}}async save(e,t){try{localStorage.setItem(`${this.storageKey}:${e}`,JSON.stringify(t))}catch(a){console.error(`Failed to save panel layout for ${e}:`,a)}}}function V(c){const{viewKey:e,defaultSizes:t,panelType:a,adapter:r=new I}=c,[f,u]=s.useState(t),[y,z]=s.useState(c.collapsed),S=s.useRef(c.collapsed),C=s.useRef({}),h=s.useRef(null),w=s.useRef(null);s.useEffect(()=>{(async()=>{const d=await r.load(e);d!=null&&d.sizes&&(u(d.sizes),m(d.sizes))})()},[e]);const m=s.useCallback(o=>{"left"in o&&o.left>0&&(C.current.left=o.left),"middle"in o&&o.middle>0&&(C.current.middle=o.middle),"right"in o&&o.right>0&&(C.current.right=o.right)},[]),n=s.useCallback(o=>{const d=C.current[o];if(d&&d>0)return d;if(o==="left"&&"left"in t&&t.left>0)return t.left;if(o==="right"&&"right"in t&&t.right>0)return t.right},[t]),b=s.useRef(t);s.useEffect(()=>{("left"in t&&t.left!==b.current.left||"middle"in t&&"middle"in b.current&&t.middle!==b.current.middle||"right"in t&&t.right!==b.current.right)&&(u(t),m(t),b.current=t)},[t,m]),s.useEffect(()=>{const o=c.collapsed.left!==S.current.left,d="right"in c.collapsed&&"right"in S.current&&c.collapsed.right!==S.current.right;(o||d)&&(z(c.collapsed),S.current={...c.collapsed})},[c.collapsed.left,c.collapsed.right]);const W=s.useCallback(async o=>{try{await r.save(e,{sizes:o})}catch(d){console.error(`Failed to save panel preferences for ${e}:`,d)}},[e,r]),v=s.useCallback(o=>{const d={...o};let i=!0;if("left"in o){const l=!!(y!=null&&y.left),g=o.left;if(l){const k=n("left");k!==void 0&&k>0?d.left=k:i=!1}else g===0&&(i=!1)}if(a==="three-panel"&&"right"in o){const l=!!(y!=null&&y.right),g=o.right;if(l){const k=n("right");k!==void 0&&k>0?d.right=k:i=!1}else g===0&&(i=!1)}if(u(d),m(d),h.current&&(clearTimeout(h.current),h.current=null),!i){w.current=null;return}w.current=d,h.current=setTimeout(()=>{const l=w.current;l&&(W(l),w.current=null),h.current=null},500)},[y,n,a,W,m]);s.useEffect(()=>()=>{if(h.current){const o=w.current;o&&(W(o),w.current=null),clearTimeout(h.current),h.current=null}},[W]);const T=s.useCallback(async()=>{},[]),R=s.useCallback(async()=>{},[]),E=s.useCallback(async()=>{},[]),L=s.useCallback(async()=>{},[]);return a==="three-panel"?{type:"three-panel",sizes:f,collapsed:y,handlePanelResize:v,handleLeftCollapseComplete:T,handleLeftExpandComplete:R,handleRightCollapseComplete:E,handleRightExpandComplete:L}:{type:"two-panel",sizes:f,collapsed:y,handlePanelResize:v,handleLeftCollapseComplete:T,handleLeftExpandComplete:R}}class O{constructor(){P(this,"PRESETS_KEY","panel-layouts:workspace-presets");P(this,"REPO_STATE_PREFIX","panel-layouts:repo-state:")}async loadWorkspacePresets(){try{const e=localStorage.getItem(this.PRESETS_KEY);return e?JSON.parse(e):{}}catch(e){return console.error("Failed to load workspace presets:",e),{}}}async saveWorkspacePresets(e){try{localStorage.setItem(this.PRESETS_KEY,JSON.stringify(e))}catch(t){throw console.error("Failed to save workspace presets:",t),t}}async loadRepositoryState(e){try{const t=`${this.REPO_STATE_PREFIX}${e}`,a=localStorage.getItem(t);return a?JSON.parse(a):null}catch(t){return console.error(`Failed to load repository state for ${e}:`,t),null}}async saveRepositoryState(e,t){try{const a=`${this.REPO_STATE_PREFIX}${e}`;localStorage.setItem(a,JSON.stringify(t))}catch(a){throw console.error(`Failed to save repository state for ${e}:`,a),a}}async loadAllRepositoryStates(){try{const e={};for(let t=0;t<localStorage.length;t++){const a=localStorage.key(t);if(a&&a.startsWith(this.REPO_STATE_PREFIX)){const r=a.substring(this.REPO_STATE_PREFIX.length),f=localStorage.getItem(a);if(f)try{e[r]=JSON.parse(f)}catch(u){console.error(`Failed to parse repository state for ${r}:`,u)}}}return e}catch(e){return console.error("Failed to load all repository states:",e),{}}}}class p{static setAdapter(e){this.adapter=e}static async getWorkspaceLayouts(){const e=this.getBuiltInWorkspaceLayouts();return{...await this.adapter.loadWorkspacePresets(),...e}}static async getWorkspaceLayout(e){return(await this.getWorkspaceLayouts())[e]||null}static async createWorkspaceLayout(e){const t=await this.adapter.loadWorkspacePresets(),a=this.generateWorkspaceId(e.name),r={id:a,name:e.name,description:e.description,layout:e.layout,defaultSizes:e.defaultSizes,defaultCollapsed:e.defaultCollapsed,createdAt:Date.now(),updatedAt:Date.now(),isBuiltIn:!1},f={...t,[a]:r};return await this.adapter.saveWorkspacePresets(f),r}static async updateWorkspaceLayout(e,t){const a=await this.adapter.loadWorkspacePresets(),r=a[e];if(!r)return console.error(`Workspace layout ${e} not found`),null;if(r.isBuiltIn)return console.error(`Cannot update built-in workspace layout ${e}`),null;const f={...r,...t,id:e,createdAt:r.createdAt,updatedAt:Date.now()},u={...a,[e]:f};return await this.adapter.saveWorkspacePresets(u),f}static async deleteWorkspaceLayout(e){const t=await this.adapter.loadWorkspacePresets(),a=t[e];if(!a)return console.error(`Workspace layout ${e} not found`),!1;if(a.isBuiltIn)return console.error(`Cannot delete built-in workspace layout ${e}`),!1;const r={...t};return delete r[e],await this.adapter.saveWorkspacePresets(r),!0}static async getRepositoryState(e){return await this.adapter.loadRepositoryState(e)}static async setRepositoryState(e,t){await this.adapter.saveRepositoryState(e,t)}static async updateRepositorySizes(e,t){const a=await this.getRepositoryState(e);a&&await this.setRepositoryState(e,{...a,sizes:t})}static async updateRepositoryCollapsed(e,t){const a=await this.getRepositoryState(e);a&&await this.setRepositoryState(e,{...a,collapsed:t})}static hasStateDeviation(e,t){const a=t.defaultSizes?JSON.stringify(e.sizes)!==JSON.stringify(t.defaultSizes):!1,r=t.defaultCollapsed?JSON.stringify(e.collapsed)!==JSON.stringify(t.defaultCollapsed):!1;return{hasSizeDeviation:a,hasCollapsedDeviation:r}}static async updateWorkspaceFromRepositoryState(e,t){const a=await this.getWorkspaceLayout(e),r=await this.getRepositoryState(t);if(!a||!r||a.isBuiltIn){console.error("Cannot update built-in workspace or workspace/state not found");return}await this.updateWorkspaceLayout(e,{defaultSizes:r.sizes,defaultCollapsed:r.collapsed})}static async resetRepositoryToWorkspaceDefaults(e,t){const a=await this.getWorkspaceLayout(t);if(!a){console.error(`Workspace ${t} not found`);return}const r=await this.getRepositoryState(e);r&&await this.setRepositoryState(e,{...r,sizes:a.defaultSizes||{left:20,middle:45,right:35},collapsed:a.defaultCollapsed||{left:!1,right:!1}})}static isLayoutMatchingWorkspace(e,t){return this.areLayoutsEqual(e,t.layout)}static async findMatchingWorkspace(e){const t=await this.getWorkspaceLayouts();for(const[a,r]of Object.entries(t))if(this.isLayoutMatchingWorkspace(e,r))return a;return null}static areLayoutsEqual(e,t){return JSON.stringify(e)===JSON.stringify(t)}static generateWorkspaceId(e){return`${e.toLowerCase().replace(/[^a-z0-9]+/g,"-")}-${Date.now()}`}static getBuiltInWorkspaceLayouts(){const e=Date.now();return{"project-management":{id:"project-management",name:"Project Management",description:"Tasks, dependencies, issues, file tree, docs, drawings, multi terminal, city visualization, code viewer, markdown slides, and excalidraw",layout:{left:{type:"tabs",panels:["tasks","dependencies","gitIssues","fileTree","docs","drawings"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"multiTerminal",right:{type:"tabs",panels:["cityVisualization","codeViewer","markdownViewer","excalidrawDiagram"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:45,right:35},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0},"code-review":{id:"code-review",name:"Code Review",description:"Git changes, pull requests, and file tree on left, git diff and code viewer in middle, city map on right",layout:{left:{type:"tabs",panels:["gitChanges","gitPullRequests","fileTree"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:{type:"tabs",panels:["gitDiff","codeViewer"],config:{defaultActiveTab:0,tabPosition:"top"}},right:"cityVisualization"},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0},documentation:{id:"documentation",name:"Documentation",description:"Docs, markdown viewer, and code viewer",layout:{left:"docs",middle:"markdownViewer",right:"codeViewer"},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},"agent-work":{id:"agent-work",name:"Agent Work",description:"Tasks, agent sessions, file tree, agent context, git changes, docs, multi terminal, city map, agent events, code viewer, and markdown slides",layout:{left:{type:"tabs",panels:["tasks","agentSessions","fileTree","agentContext","gitChanges","docs"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"multiTerminal",right:{type:"tabs",panels:["cityVisualization","agentEvents","codeViewer","markdownViewer"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:45,right:35},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0},"quality-check":{id:"quality-check",name:"Quality Check",description:"Package information, tools, and dependencies on left; city visualization map in middle; multi terminal and code viewer on right (collapsed)",layout:{left:{type:"tabs",panels:["packageInfo","tools","dependencies"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"cityVisualization",right:{type:"tabs",panels:["multiTerminal","codeViewer"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:45,right:35},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},drawing:{id:"drawing",name:"Drawing",description:"Drawings and docs, excalidraw diagram, multi terminal and markdown viewer",layout:{left:{type:"tabs",panels:["drawings","docs"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:"excalidrawDiagram",right:{type:"tabs",panels:["multiTerminal","markdownViewer"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},"old-school":{id:"old-school",name:"Old School",description:"File tree, search, git changes, and docs on left; code viewer and markdown viewer in middle; multi terminal and city map on right (collapsed)",layout:{left:{type:"tabs",panels:["fileTree","search","gitChanges","docs"],config:{defaultActiveTab:0,tabPosition:"top"}},middle:{type:"tabs",panels:["codeViewer","markdownViewer"],config:{defaultActiveTab:0,tabPosition:"top"}},right:{type:"tabs",panels:["multiTerminal","cityVisualization"],config:{defaultActiveTab:0,tabPosition:"top"}}},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!0},createdAt:e,updatedAt:e,isBuiltIn:!0},"principal-office":{id:"principal-office",name:"Principal Office",description:"Alexandria docs on left, MDX editor in middle, multi terminal on right",layout:{left:"docs",middle:"mdxEditor",right:"multiTerminal"},defaultSizes:{left:20,middle:50,right:30},defaultCollapsed:{left:!1,right:!1},createdAt:e,updatedAt:e,isBuiltIn:!0}}}static async initializeWorkspaceLayouts(){const e=await this.adapter.loadWorkspacePresets();Object.keys(e).length}}P(p,"adapter",new O);function F(c={}){const{repositoryKey:e,autoInitialize:t=!1,defaultWorkspaceId:a}=c,[r,f]=s.useState({}),[u,y]=s.useState(null),[z,S]=s.useState(null),[C,h]=s.useState(!0),[w,m]=s.useState(null),n=s.useCallback(async()=>{try{h(!0),m(null);const i=await p.getWorkspaceLayouts();if(f(i),e){let l=await p.getRepositoryState(e);if(!l&&t){const g=a||"project-management",k=await p.getWorkspaceLayout(g);k&&(l={workspaceId:g,sizes:k.defaultSizes||{left:20,middle:45,right:35},collapsed:k.defaultCollapsed||{left:!1,right:!1}},await p.setRepositoryState(e,l))}if(y(l),l!=null&&l.workspaceId){const g=await p.getWorkspaceLayout(l.workspaceId);S(g)}else S(null)}}catch(i){m(i),console.error("Failed to load workspace data:",i)}finally{h(!1)}},[e,t,a]);s.useEffect(()=>{n()},[n]);const b=s.useCallback(async i=>await p.getWorkspaceLayout(i),[]),W=s.useCallback(async i=>{const l=await p.createWorkspaceLayout(i);return await n(),l},[n]),v=s.useCallback(async(i,l)=>{const g=await p.updateWorkspaceLayout(i,l);return await n(),g},[n]),T=s.useCallback(async i=>{const l=await p.deleteWorkspaceLayout(i);return l&&await n(),l},[n]),R=s.useCallback(async i=>{if(!e){console.error("Cannot apply workspace without repositoryKey");return}const l=await p.getWorkspaceLayout(i);if(!l){console.error(`Workspace ${i} not found`);return}const g={workspaceId:i,sizes:l.defaultSizes||{left:20,middle:45,right:35},collapsed:l.defaultCollapsed||{left:!1,right:!1}};await p.setRepositoryState(e,g),await n()},[e,n]),E=s.useCallback(async i=>{if(!e){console.error("Cannot update sizes without repositoryKey");return}await p.updateRepositorySizes(e,i),await n()},[e,n]),L=s.useCallback(async i=>{if(!e){console.error("Cannot update collapsed without repositoryKey");return}await p.updateRepositoryCollapsed(e,i),await n()},[e,n]),o=s.useCallback(async()=>{if(!e||!(u!=null&&u.workspaceId)){console.error("Cannot reset without repositoryKey and active workspace");return}await p.resetRepositoryToWorkspaceDefaults(e,u.workspaceId),await n()},[e,u==null?void 0:u.workspaceId,n]),d=s.useCallback(async()=>{await n()},[n]);return{workspaces:r,repositoryState:u,activeWorkspace:z,loading:C,error:w,getWorkspace:b,createWorkspace:W,updateWorkspace:v,deleteWorkspace:T,applyWorkspace:R,updateSizes:E,updateCollapsed:L,resetToDefaults:o,refresh:d}}Object.defineProperty(exports,"EditableConfigurablePanelLayout",{enumerable:!0,get:()=>A.EditableConfigurablePanelLayout});Object.defineProperty(exports,"ResponsiveConfigurablePanelLayout",{enumerable:!0,get:()=>A.ResponsiveConfigurablePanelLayout});Object.defineProperty(exports,"mapThemeToPanelVars",{enumerable:!0,get:()=>A.mapThemeToPanelVars});Object.defineProperty(exports,"mapThemeToTabVars",{enumerable:!0,get:()=>A.mapThemeToTabVars});exports.LocalStoragePersistenceAdapter=I;exports.LocalStorageWorkspaceAdapter=O;exports.WorkspaceLayoutService=p;exports.usePanelPersistence=V;exports.useWorkspace=F;
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/hooks/usePanelPersistence.ts","../src/services/LocalStorageWorkspaceAdapter.ts","../src/services/WorkspaceLayoutService.ts","../src/hooks/useWorkspace.ts"],"sourcesContent":["/**\n * usePanelPersistence Hook\n *\n * Extracted and adapted from electron-app/src/renderer/hooks/usePanelPersistence.ts\n * Manages saving and loading panel sizes and collapsed states with debounced persistence.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type {\n UsePanelPersistenceOptions,\n PanelSizes,\n TwoPanelSizes,\n PanelCollapsed,\n PanelPersistence,\n PersistenceAdapter,\n} from '../types/persistence.types';\n\n/**\n * Default localStorage-based persistence adapter for web applications\n */\nexport class LocalStoragePersistenceAdapter implements PersistenceAdapter {\n private storageKey = 'panel-layouts';\n\n async load(viewKey: string) {\n try {\n const stored = localStorage.getItem(`${this.storageKey}:${viewKey}`);\n return stored ? JSON.parse(stored) : null;\n } catch (error) {\n console.error(`Failed to load panel layout for ${viewKey}:`, error);\n return null;\n }\n }\n\n async save(viewKey: string, state: { sizes: PanelSizes | TwoPanelSizes }) {\n try {\n localStorage.setItem(\n `${this.storageKey}:${viewKey}`,\n JSON.stringify(state),\n );\n } catch (error) {\n console.error(`Failed to save panel layout for ${viewKey}:`, error);\n }\n }\n}\n\n/**\n * Hook for persisting panel layouts across sessions\n *\n * Manages panel sizes and collapsed states with automatic debounced saving.\n * Supports both 2-panel and 3-panel layouts.\n *\n * @param options - Configuration options for persistence\n * @returns Panel state and handlers for resizing and collapsing\n *\n * @example\n * ```tsx\n * const persistence = usePanelPersistence({\n * viewKey: 'my-app',\n * defaultSizes: { left: 30, middle: 70, right: 0 },\n * collapsed: { left: false, right: true },\n * panelType: 'three-panel',\n * });\n *\n * <ConfigurablePanelLayout\n * {...otherProps}\n * defaultSizes={persistence.sizes}\n * collapsed={persistence.collapsed}\n * onPanelResize={persistence.handlePanelResize}\n * />\n * ```\n */\nexport function usePanelPersistence(\n options: UsePanelPersistenceOptions,\n): PanelPersistence {\n const {\n viewKey,\n defaultSizes,\n panelType,\n adapter = new LocalStoragePersistenceAdapter(),\n } = options;\n\n const [sizes, setSizes] = useState(defaultSizes);\n const [collapsed, setCollapsed] = useState(options.collapsed);\n const prevCollapsedRef = useRef(options.collapsed);\n const lastNonZeroSizesRef = useRef<Partial<PanelSizes & TwoPanelSizes>>({});\n const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingPersistSizesRef = useRef<typeof sizes | null>(null);\n\n // Load saved state on mount\n useEffect(() => {\n const loadSavedState = async () => {\n const saved = await adapter.load(viewKey);\n if (saved?.sizes) {\n setSizes(saved.sizes as typeof sizes);\n updateLastNonZeroSizes(saved.sizes);\n }\n };\n loadSavedState();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [viewKey]);\n\n const updateLastNonZeroSizes = useCallback(\n (incomingSizes: PanelSizes | TwoPanelSizes) => {\n if ('left' in incomingSizes && incomingSizes.left > 0) {\n lastNonZeroSizesRef.current.left = incomingSizes.left;\n }\n\n if ('middle' in incomingSizes && incomingSizes.middle > 0) {\n lastNonZeroSizesRef.current.middle = incomingSizes.middle;\n }\n\n if ('right' in incomingSizes && incomingSizes.right > 0) {\n lastNonZeroSizesRef.current.right = incomingSizes.right;\n }\n },\n [],\n );\n\n const getFallbackSize = useCallback(\n (panel: 'left' | 'right') => {\n const storedSize = lastNonZeroSizesRef.current[panel];\n if (storedSize && storedSize > 0) {\n return storedSize;\n }\n\n if (panel === 'left' && 'left' in defaultSizes && defaultSizes.left > 0) {\n return defaultSizes.left;\n }\n\n if (\n panel === 'right' &&\n 'right' in defaultSizes &&\n defaultSizes.right > 0\n ) {\n return defaultSizes.right;\n }\n\n return undefined;\n },\n [defaultSizes],\n );\n\n // Update sizes when defaultSizes changes (parent has loaded preferences)\n const prevDefaultSizesRef = useRef(defaultSizes);\n useEffect(() => {\n // Only update if the actual values changed, not just the reference\n const hasChanged =\n ('left' in defaultSizes &&\n defaultSizes.left !== prevDefaultSizesRef.current.left) ||\n ('middle' in defaultSizes &&\n 'middle' in prevDefaultSizesRef.current &&\n defaultSizes.middle !==\n (prevDefaultSizesRef.current as PanelSizes).middle) ||\n ('right' in defaultSizes &&\n defaultSizes.right !== prevDefaultSizesRef.current.right);\n\n if (hasChanged) {\n setSizes(defaultSizes);\n updateLastNonZeroSizes(defaultSizes);\n prevDefaultSizesRef.current = defaultSizes;\n }\n }, [defaultSizes, updateLastNonZeroSizes]);\n\n // Sync with parent's collapsed state (e.g., from titlebar buttons)\n useEffect(() => {\n const leftChanged =\n options.collapsed.left !== prevCollapsedRef.current.left;\n const rightChanged =\n 'right' in options.collapsed &&\n 'right' in prevCollapsedRef.current &&\n (options.collapsed as PanelCollapsed).right !==\n (prevCollapsedRef.current as PanelCollapsed).right;\n\n if (leftChanged || rightChanged) {\n setCollapsed(options.collapsed);\n prevCollapsedRef.current = { ...options.collapsed };\n }\n }, [options.collapsed.left, (options.collapsed as PanelCollapsed).right]);\n\n // Save preferences helper (only saves sizes, not collapsed state)\n const savePreferences = useCallback(\n async (newSizes: typeof sizes) => {\n try {\n await adapter.save(viewKey, { sizes: newSizes });\n } catch (error) {\n console.error(\n `Failed to save panel preferences for ${viewKey}:`,\n error,\n );\n }\n },\n [viewKey, adapter],\n );\n\n // Handle panel resize (debounced)\n const handlePanelResize = useCallback(\n (newSizes: typeof sizes) => {\n const sanitizedSizes = { ...newSizes } as typeof newSizes;\n let shouldPersist = true;\n\n if ('left' in newSizes) {\n const leftCollapsed = Boolean((collapsed as PanelCollapsed)?.left);\n const leftSize = newSizes.left;\n\n if (leftCollapsed) {\n const fallback = getFallbackSize('left');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.left = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (leftSize === 0) {\n shouldPersist = false;\n }\n }\n\n if (panelType === 'three-panel' && 'right' in newSizes) {\n const rightCollapsed = Boolean((collapsed as PanelCollapsed)?.right);\n const rightSize = newSizes.right;\n\n if (rightCollapsed) {\n const fallback = getFallbackSize('right');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.right = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (rightSize === 0) {\n shouldPersist = false;\n }\n }\n\n setSizes(sanitizedSizes);\n updateLastNonZeroSizes(sanitizedSizes);\n\n if (saveTimeoutRef.current) {\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n\n if (!shouldPersist) {\n pendingPersistSizesRef.current = null;\n return;\n }\n\n pendingPersistSizesRef.current = sanitizedSizes;\n saveTimeoutRef.current = setTimeout(() => {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n saveTimeoutRef.current = null;\n }, 500);\n },\n [\n collapsed,\n getFallbackSize,\n panelType,\n savePreferences,\n updateLastNonZeroSizes,\n ],\n );\n\n // Cleanup: save any pending changes on unmount\n useEffect(() => {\n return () => {\n if (saveTimeoutRef.current) {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n };\n }, [savePreferences]);\n\n // Collapse/expand handlers - no-ops because state is controlled by parent\n // The parent manages collapsed state via titlebar buttons and passes it down as props\n const handleLeftCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleLeftExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n if (panelType === 'three-panel') {\n return {\n type: 'three-panel',\n sizes: sizes as PanelSizes,\n collapsed: collapsed as PanelCollapsed,\n handlePanelResize: handlePanelResize as (sizes: PanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n handleRightCollapseComplete,\n handleRightExpandComplete,\n };\n } else {\n return {\n type: 'two-panel',\n sizes: sizes as TwoPanelSizes,\n collapsed: collapsed as { left?: boolean },\n handlePanelResize: handlePanelResize as (sizes: TwoPanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n };\n }\n}\n","/**\n * LocalStorageWorkspaceAdapter - Browser localStorage implementation\n * Stores workspace presets and repository states in browser localStorage\n */\n\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n} from '../types/workspace.types';\n\nexport class LocalStorageWorkspaceAdapter implements WorkspaceStorageAdapter {\n private readonly PRESETS_KEY = 'panel-layouts:workspace-presets';\n private readonly REPO_STATE_PREFIX = 'panel-layouts:repo-state:';\n\n /**\n * Load all user-created workspace presets\n */\n async loadWorkspacePresets(): Promise<Record<string, WorkspaceLayout>> {\n try {\n const stored = localStorage.getItem(this.PRESETS_KEY);\n if (!stored) {\n return {};\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error('Failed to load workspace presets:', error);\n return {};\n }\n }\n\n /**\n * Save workspace presets\n */\n async saveWorkspacePresets(\n presets: Record<string, WorkspaceLayout>,\n ): Promise<void> {\n try {\n localStorage.setItem(this.PRESETS_KEY, JSON.stringify(presets));\n } catch (error) {\n console.error('Failed to save workspace presets:', error);\n throw error;\n }\n }\n\n /**\n * Load repository-specific workspace state\n */\n async loadRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n const stored = localStorage.getItem(key);\n if (!stored) {\n return null;\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error(\n `Failed to load repository state for ${repositoryKey}:`,\n error,\n );\n return null;\n }\n }\n\n /**\n * Save repository-specific workspace state\n */\n async saveRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n localStorage.setItem(key, JSON.stringify(state));\n } catch (error) {\n console.error(\n `Failed to save repository state for ${repositoryKey}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Load all repository states\n */\n async loadAllRepositoryStates(): Promise<\n Record<string, RepositoryWorkspaceState>\n > {\n try {\n const allStates: Record<string, RepositoryWorkspaceState> = {};\n\n // Iterate through all localStorage keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(this.REPO_STATE_PREFIX)) {\n const repositoryKey = key.substring(this.REPO_STATE_PREFIX.length);\n const stored = localStorage.getItem(key);\n if (stored) {\n try {\n allStates[repositoryKey] = JSON.parse(stored);\n } catch (parseError) {\n console.error(\n `Failed to parse repository state for ${repositoryKey}:`,\n parseError,\n );\n }\n }\n }\n }\n\n return allStates;\n } catch (error) {\n console.error('Failed to load all repository states:', error);\n return {};\n }\n }\n}\n","/**\n * WorkspaceLayoutService - Manages workspace layout presets\n * Extracted from electron-app/src/renderer/services/WorkspaceLayoutService.ts\n *\n * This service handles CRUD operations for workspace layouts (panel configuration presets)\n * and provides utilities for comparing layouts and managing built-in workspaces.\n */\n\nimport type { PanelLayout } from '@a24z/panels';\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\nimport { LocalStorageWorkspaceAdapter } from './LocalStorageWorkspaceAdapter';\n\nexport class WorkspaceLayoutService {\n private static adapter: WorkspaceStorageAdapter =\n new LocalStorageWorkspaceAdapter();\n\n /**\n * Configure a custom storage adapter (for Electron IPC or remote storage)\n */\n static setAdapter(adapter: WorkspaceStorageAdapter): void {\n this.adapter = adapter;\n }\n\n /**\n * Get all workspace layouts (including built-in)\n */\n static async getWorkspaceLayouts(): Promise<Record<string, WorkspaceLayout>> {\n const builtInWorkspaces = this.getBuiltInWorkspaceLayouts();\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Merge built-in and user workspaces, built-in takes precedence\n return {\n ...userWorkspaces,\n ...builtInWorkspaces,\n };\n }\n\n /**\n * Get a specific workspace layout by ID\n */\n static async getWorkspaceLayout(id: string): Promise<WorkspaceLayout | null> {\n const workspaces = await this.getWorkspaceLayouts();\n return workspaces[id] || null;\n }\n\n /**\n * Create a new workspace layout\n */\n static async createWorkspaceLayout(\n options: CreateWorkspaceOptions,\n ): Promise<WorkspaceLayout> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const id = this.generateWorkspaceId(options.name);\n\n const workspace: WorkspaceLayout = {\n id,\n name: options.name,\n description: options.description,\n layout: options.layout,\n defaultSizes: options.defaultSizes,\n defaultCollapsed: options.defaultCollapsed,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n isBuiltIn: false,\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: workspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return workspace;\n }\n\n /**\n * Update an existing workspace layout\n */\n static async updateWorkspaceLayout(\n id: string,\n updates: UpdateWorkspaceOptions,\n ): Promise<WorkspaceLayout | null> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const existingWorkspace = userWorkspaces[id];\n\n if (!existingWorkspace) {\n console.error(`Workspace layout ${id} not found`);\n return null;\n }\n\n // Don't allow updating built-in workspaces\n if (existingWorkspace.isBuiltIn) {\n console.error(`Cannot update built-in workspace layout ${id}`);\n return null;\n }\n\n const updatedWorkspace: WorkspaceLayout = {\n ...existingWorkspace,\n ...updates,\n id, // Preserve ID\n createdAt: existingWorkspace.createdAt, // Preserve creation time\n updatedAt: Date.now(),\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: updatedWorkspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return updatedWorkspace;\n }\n\n /**\n * Delete a workspace layout\n */\n static async deleteWorkspaceLayout(id: string): Promise<boolean> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const workspace = userWorkspaces[id];\n\n if (!workspace) {\n console.error(`Workspace layout ${id} not found`);\n return false;\n }\n\n // Don't allow deleting built-in workspaces\n if (workspace.isBuiltIn) {\n console.error(`Cannot delete built-in workspace layout ${id}`);\n return false;\n }\n\n const updatedPresets = { ...userWorkspaces };\n delete updatedPresets[id];\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return true;\n }\n\n /**\n * Get repository state (which workspace + current sizes/collapsed)\n */\n static async getRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n return await this.adapter.loadRepositoryState(repositoryKey);\n }\n\n /**\n * Set repository state (which workspace + current sizes/collapsed/layout)\n */\n static async setRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n await this.adapter.saveRepositoryState(repositoryKey, state);\n }\n\n /**\n * Update only sizes in repository state\n */\n static async updateRepositorySizes(\n repositoryKey: string,\n sizes: { left: number; middle: number; right: number },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes,\n });\n }\n\n /**\n * Update only collapsed state in repository state\n */\n static async updateRepositoryCollapsed(\n repositoryKey: string,\n collapsed: { left?: boolean; right?: boolean },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n collapsed,\n });\n }\n\n /**\n * Check if repository state differs from workspace defaults\n */\n static hasStateDeviation(\n repoState: {\n workspaceId: string | null;\n sizes: { left: number; middle: number; right: number };\n collapsed: { left?: boolean; right?: boolean };\n },\n workspace: WorkspaceLayout,\n ): {\n hasSizeDeviation: boolean;\n hasCollapsedDeviation: boolean;\n } {\n const hasSizeDeviation = workspace.defaultSizes\n ? JSON.stringify(repoState.sizes) !==\n JSON.stringify(workspace.defaultSizes)\n : false;\n\n const hasCollapsedDeviation = workspace.defaultCollapsed\n ? JSON.stringify(repoState.collapsed) !==\n JSON.stringify(workspace.defaultCollapsed)\n : false;\n\n return { hasSizeDeviation, hasCollapsedDeviation };\n }\n\n /**\n * Update workspace defaults from repository state\n */\n static async updateWorkspaceFromRepositoryState(\n workspaceId: string,\n repositoryKey: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n const repoState = await this.getRepositoryState(repositoryKey);\n\n if (!workspace || !repoState || workspace.isBuiltIn) {\n console.error(\n 'Cannot update built-in workspace or workspace/state not found',\n );\n return;\n }\n\n await this.updateWorkspaceLayout(workspaceId, {\n defaultSizes: repoState.sizes,\n defaultCollapsed: repoState.collapsed,\n });\n }\n\n /**\n * Reset repository state to workspace defaults\n */\n static async resetRepositoryToWorkspaceDefaults(\n repositoryKey: string,\n workspaceId: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || { left: false, right: false },\n });\n }\n\n /**\n * Check if a layout matches a workspace layout\n */\n static isLayoutMatchingWorkspace(\n layout: PanelLayout,\n workspace: WorkspaceLayout,\n ): boolean {\n return this.areLayoutsEqual(layout, workspace.layout);\n }\n\n /**\n * Find workspace ID that matches the given layout\n */\n static async findMatchingWorkspace(\n layout: PanelLayout,\n ): Promise<string | null> {\n const workspaces = await this.getWorkspaceLayouts();\n\n for (const [id, workspace] of Object.entries(workspaces)) {\n if (this.isLayoutMatchingWorkspace(layout, workspace)) {\n return id;\n }\n }\n\n return null;\n }\n\n /**\n * Deep comparison of two panel layouts\n */\n private static areLayoutsEqual(\n layout1: PanelLayout,\n layout2: PanelLayout,\n ): boolean {\n return JSON.stringify(layout1) === JSON.stringify(layout2);\n }\n\n /**\n * Generate a unique ID from a workspace name\n */\n private static generateWorkspaceId(name: string): string {\n const base = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n return `${base}-${Date.now()}`;\n }\n\n /**\n * Get built-in workspace layouts\n */\n static getBuiltInWorkspaceLayouts(): Record<string, WorkspaceLayout> {\n const now = Date.now();\n\n return {\n 'project-management': {\n id: 'project-management',\n name: 'Project Management',\n description:\n 'Tasks, dependencies, issues, file tree, docs, drawings, multi terminal, city visualization, code viewer, markdown slides, and excalidraw',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'dependencies',\n 'gitIssues',\n 'fileTree',\n 'docs',\n 'drawings',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'codeViewer',\n 'markdownViewer',\n 'excalidrawDiagram',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'code-review': {\n id: 'code-review',\n name: 'Code Review',\n description:\n 'Git changes, pull requests, and file tree on left, git diff and code viewer in middle, city map on right',\n layout: {\n left: {\n type: 'tabs',\n panels: ['gitChanges', 'gitPullRequests', 'fileTree'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['gitDiff', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: 'cityVisualization',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n documentation: {\n id: 'documentation',\n name: 'Documentation',\n description: 'Docs, markdown viewer, and code viewer',\n layout: {\n left: 'docs',\n middle: 'markdownViewer',\n right: 'codeViewer',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'agent-work': {\n id: 'agent-work',\n name: 'Agent Work',\n description:\n 'Tasks, agent sessions, file tree, agent context, git changes, docs, multi terminal, city map, agent events, code viewer, and markdown slides',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'agentSessions',\n 'fileTree',\n 'agentContext',\n 'gitChanges',\n 'docs',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'agentEvents',\n 'codeViewer',\n 'markdownViewer',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'quality-check': {\n id: 'quality-check',\n name: 'Quality Check',\n description:\n 'Package information, tools, and dependencies on left; city visualization map in middle; multi terminal and code viewer on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['packageInfo', 'tools', 'dependencies'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'cityVisualization',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n drawing: {\n id: 'drawing',\n name: 'Drawing',\n description:\n 'Drawings and docs, excalidraw diagram, multi terminal and markdown viewer',\n layout: {\n left: {\n type: 'tabs',\n panels: ['drawings', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'excalidrawDiagram',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'old-school': {\n id: 'old-school',\n name: 'Old School',\n description:\n 'File tree, search, git changes, and docs on left; code viewer and markdown viewer in middle; multi terminal and city map on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['fileTree', 'search', 'gitChanges', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['codeViewer', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'cityVisualization'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'principal-office': {\n id: 'principal-office',\n name: 'Principal Office',\n description:\n 'Alexandria docs on left, MDX editor in middle, multi terminal on right',\n layout: {\n left: 'docs',\n middle: 'mdxEditor',\n right: 'multiTerminal',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n };\n }\n\n /**\n * Initialize workspace layouts with built-in defaults if none exist\n */\n static async initializeWorkspaceLayouts(): Promise<void> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Only initialize if there are no user workspaces yet\n // Built-in workspaces are always available via getBuiltInWorkspaceLayouts()\n if (Object.keys(userWorkspaces).length === 0) {\n // Nothing to do - built-in workspaces are always available\n // This method exists for compatibility with electron-app\n }\n }\n}\n","/**\n * useWorkspace - React hook for workspace management\n * Provides easy access to workspace operations and state\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { WorkspaceLayoutService } from '../services/WorkspaceLayoutService';\nimport type {\n WorkspaceLayout,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\n\nexport interface UseWorkspaceOptions {\n /**\n * Optional repository key for repository-specific state\n * If provided, the hook will manage repository state\n */\n repositoryKey?: string;\n\n /**\n * Auto-initialize repository state if it doesn't exist\n */\n autoInitialize?: boolean;\n\n /**\n * Default workspace to use for auto-initialization\n */\n defaultWorkspaceId?: string;\n}\n\nexport interface UseWorkspaceReturn {\n /**\n * All available workspaces (built-in + user-created)\n */\n workspaces: Record<string, WorkspaceLayout>;\n\n /**\n * Current repository state (if repositoryKey provided)\n */\n repositoryState: RepositoryWorkspaceState | null;\n\n /**\n * Current active workspace (if repositoryKey provided)\n */\n activeWorkspace: WorkspaceLayout | null;\n\n /**\n * Loading state\n */\n loading: boolean;\n\n /**\n * Error state\n */\n error: Error | null;\n\n /**\n * Get a specific workspace by ID\n */\n getWorkspace: (id: string) => Promise<WorkspaceLayout | null>;\n\n /**\n * Create a new workspace\n */\n createWorkspace: (options: CreateWorkspaceOptions) => Promise<WorkspaceLayout>;\n\n /**\n * Update an existing workspace\n */\n updateWorkspace: (\n id: string,\n updates: UpdateWorkspaceOptions,\n ) => Promise<WorkspaceLayout | null>;\n\n /**\n * Delete a workspace\n */\n deleteWorkspace: (id: string) => Promise<boolean>;\n\n /**\n * Apply a workspace to the current repository\n */\n applyWorkspace: (workspaceId: string) => Promise<void>;\n\n /**\n * Update repository sizes\n */\n updateSizes: (sizes: {\n left: number;\n middle: number;\n right: number;\n }) => Promise<void>;\n\n /**\n * Update repository collapsed state\n */\n updateCollapsed: (collapsed: {\n left?: boolean;\n right?: boolean;\n }) => Promise<void>;\n\n /**\n * Reset repository to workspace defaults\n */\n resetToDefaults: () => Promise<void>;\n\n /**\n * Refresh workspace data\n */\n refresh: () => Promise<void>;\n}\n\nexport function useWorkspace(\n options: UseWorkspaceOptions = {},\n): UseWorkspaceReturn {\n const { repositoryKey, autoInitialize = false, defaultWorkspaceId } = options;\n\n const [workspaces, setWorkspaces] = useState<Record<string, WorkspaceLayout>>(\n {},\n );\n const [repositoryState, setRepositoryState] =\n useState<RepositoryWorkspaceState | null>(null);\n const [activeWorkspace, setActiveWorkspace] =\n useState<WorkspaceLayout | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n /**\n * Load all workspaces and repository state\n */\n const loadData = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n\n // Load all workspaces\n const allWorkspaces =\n await WorkspaceLayoutService.getWorkspaceLayouts();\n setWorkspaces(allWorkspaces);\n\n // Load repository state if repositoryKey provided\n if (repositoryKey) {\n let repoState =\n await WorkspaceLayoutService.getRepositoryState(repositoryKey);\n\n // Auto-initialize if requested and state doesn't exist\n if (!repoState && autoInitialize) {\n const workspaceId =\n defaultWorkspaceId || 'project-management'; // Default to project-management\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n\n if (workspace) {\n repoState = {\n workspaceId,\n sizes: workspace.defaultSizes || {\n left: 20,\n middle: 45,\n right: 35,\n },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(\n repositoryKey,\n repoState,\n );\n }\n }\n\n setRepositoryState(repoState);\n\n // Load active workspace if repository has one\n if (repoState?.workspaceId) {\n const workspace = await WorkspaceLayoutService.getWorkspaceLayout(\n repoState.workspaceId,\n );\n setActiveWorkspace(workspace);\n } else {\n setActiveWorkspace(null);\n }\n }\n } catch (err) {\n setError(err as Error);\n console.error('Failed to load workspace data:', err);\n } finally {\n setLoading(false);\n }\n }, [repositoryKey, autoInitialize, defaultWorkspaceId]);\n\n /**\n * Initial load\n */\n useEffect(() => {\n loadData();\n }, [loadData]);\n\n /**\n * Get a specific workspace\n */\n const getWorkspace = useCallback(\n async (id: string) => {\n return await WorkspaceLayoutService.getWorkspaceLayout(id);\n },\n [],\n );\n\n /**\n * Create a new workspace\n */\n const createWorkspace = useCallback(\n async (createOptions: CreateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.createWorkspaceLayout(createOptions);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Update an existing workspace\n */\n const updateWorkspace = useCallback(\n async (id: string, updates: UpdateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.updateWorkspaceLayout(id, updates);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Delete a workspace\n */\n const deleteWorkspace = useCallback(\n async (id: string) => {\n const success = await WorkspaceLayoutService.deleteWorkspaceLayout(id);\n if (success) {\n await loadData(); // Refresh\n }\n return success;\n },\n [loadData],\n );\n\n /**\n * Apply a workspace to the current repository\n */\n const applyWorkspace = useCallback(\n async (workspaceId: string) => {\n if (!repositoryKey) {\n console.error('Cannot apply workspace without repositoryKey');\n return;\n }\n\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const newState: RepositoryWorkspaceState = {\n workspaceId,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(repositoryKey, newState);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository sizes\n */\n const updateSizes = useCallback(\n async (sizes: { left: number; middle: number; right: number }) => {\n if (!repositoryKey) {\n console.error('Cannot update sizes without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositorySizes(repositoryKey, sizes);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository collapsed state\n */\n const updateCollapsed = useCallback(\n async (collapsed: { left?: boolean; right?: boolean }) => {\n if (!repositoryKey) {\n console.error('Cannot update collapsed without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositoryCollapsed(\n repositoryKey,\n collapsed,\n );\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Reset repository to workspace defaults\n */\n const resetToDefaults = useCallback(async () => {\n if (!repositoryKey || !repositoryState?.workspaceId) {\n console.error('Cannot reset without repositoryKey and active workspace');\n return;\n }\n\n await WorkspaceLayoutService.resetRepositoryToWorkspaceDefaults(\n repositoryKey,\n repositoryState.workspaceId,\n );\n await loadData(); // Refresh\n }, [repositoryKey, repositoryState?.workspaceId, loadData]);\n\n /**\n * Refresh workspace data\n */\n const refresh = useCallback(async () => {\n await loadData();\n }, [loadData]);\n\n return {\n workspaces,\n repositoryState,\n activeWorkspace,\n loading,\n error,\n getWorkspace,\n createWorkspace,\n updateWorkspace,\n deleteWorkspace,\n applyWorkspace,\n updateSizes,\n updateCollapsed,\n resetToDefaults,\n refresh,\n };\n}\n"],"names":["LocalStoragePersistenceAdapter","__publicField","viewKey","stored","error","state","usePanelPersistence","options","defaultSizes","panelType","adapter","sizes","setSizes","useState","collapsed","setCollapsed","prevCollapsedRef","useRef","lastNonZeroSizesRef","saveTimeoutRef","pendingPersistSizesRef","useEffect","saved","updateLastNonZeroSizes","useCallback","incomingSizes","getFallbackSize","panel","storedSize","prevDefaultSizesRef","leftChanged","rightChanged","savePreferences","newSizes","handlePanelResize","sanitizedSizes","shouldPersist","leftCollapsed","leftSize","fallback","rightCollapsed","rightSize","pendingSizes","handleLeftCollapseComplete","handleLeftExpandComplete","handleRightCollapseComplete","handleRightExpandComplete","LocalStorageWorkspaceAdapter","presets","repositoryKey","key","allStates","i","parseError","WorkspaceLayoutService","builtInWorkspaces","id","userWorkspaces","workspace","updatedPresets","updates","existingWorkspace","updatedWorkspace","currentState","repoState","hasSizeDeviation","hasCollapsedDeviation","workspaceId","layout","workspaces","layout1","layout2","name","now","useWorkspace","autoInitialize","defaultWorkspaceId","setWorkspaces","repositoryState","setRepositoryState","activeWorkspace","setActiveWorkspace","loading","setLoading","setError","loadData","allWorkspaces","err","getWorkspace","createWorkspace","createOptions","updateWorkspace","deleteWorkspace","success","applyWorkspace","newState","updateSizes","updateCollapsed","resetToDefaults","refresh"],"mappings":"uSAoBO,MAAMA,CAA6D,CAAnE,cACGC,EAAA,kBAAa,iBAErB,MAAM,KAAKC,EAAiB,CAC1B,GAAI,CACF,MAAMC,EAAS,aAAa,QAAQ,GAAG,KAAK,UAAU,IAAID,CAAO,EAAE,EACnE,OAAOC,EAAS,KAAK,MAAMA,CAAM,EAAI,IACvC,OAASC,EAAO,CACd,eAAQ,MAAM,mCAAmCF,CAAO,IAAKE,CAAK,EAC3D,IACT,CACF,CAEA,MAAM,KAAKF,EAAiBG,EAA8C,CACxE,GAAI,CACF,aAAa,QACX,GAAG,KAAK,UAAU,IAAIH,CAAO,GAC7B,KAAK,UAAUG,CAAK,CAAA,CAExB,OAASD,EAAO,CACd,QAAQ,MAAM,mCAAmCF,CAAO,IAAKE,CAAK,CACpE,CACF,CACF,CA4BO,SAASE,EACdC,EACkB,CAClB,KAAM,CACJ,QAAAL,EACA,aAAAM,EACA,UAAAC,EACA,QAAAC,EAAU,IAAIV,CAA+B,EAC3CO,EAEE,CAACI,EAAOC,CAAQ,EAAIC,EAAAA,SAASL,CAAY,EACzC,CAACM,EAAWC,CAAY,EAAIF,EAAAA,SAASN,EAAQ,SAAS,EACtDS,EAAmBC,EAAAA,OAAOV,EAAQ,SAAS,EAC3CW,EAAsBD,EAAAA,OAA4C,EAAE,EACpEE,EAAiBF,EAAAA,OAA6C,IAAI,EAClEG,EAAyBH,EAAAA,OAA4B,IAAI,EAG/DI,EAAAA,UAAU,IAAM,EACS,SAAY,CACjC,MAAMC,EAAQ,MAAMZ,EAAQ,KAAKR,CAAO,EACpCoB,GAAA,MAAAA,EAAO,QACTV,EAASU,EAAM,KAAqB,EACpCC,EAAuBD,EAAM,KAAK,EAEtC,GACA,CAEF,EAAG,CAACpB,CAAO,CAAC,EAEZ,MAAMqB,EAAyBC,EAAAA,YAC5BC,GAA8C,CACzC,SAAUA,GAAiBA,EAAc,KAAO,IAClDP,EAAoB,QAAQ,KAAOO,EAAc,MAG/C,WAAYA,GAAiBA,EAAc,OAAS,IACtDP,EAAoB,QAAQ,OAASO,EAAc,QAGjD,UAAWA,GAAiBA,EAAc,MAAQ,IACpDP,EAAoB,QAAQ,MAAQO,EAAc,MAEtD,EACA,CAAA,CAAC,EAGGC,EAAkBF,EAAAA,YACrBG,GAA4B,CAC3B,MAAMC,EAAaV,EAAoB,QAAQS,CAAK,EACpD,GAAIC,GAAcA,EAAa,EAC7B,OAAOA,EAGT,GAAID,IAAU,QAAU,SAAUnB,GAAgBA,EAAa,KAAO,EACpE,OAAOA,EAAa,KAGtB,GACEmB,IAAU,SACV,UAAWnB,GACXA,EAAa,MAAQ,EAErB,OAAOA,EAAa,KAIxB,EACA,CAACA,CAAY,CAAA,EAITqB,EAAsBZ,EAAAA,OAAOT,CAAY,EAC/Ca,EAAAA,UAAU,IAAM,EAGX,SAAUb,GACTA,EAAa,OAASqB,EAAoB,QAAQ,MACnD,WAAYrB,GACX,WAAYqB,EAAoB,SAChCrB,EAAa,SACVqB,EAAoB,QAAuB,QAC/C,UAAWrB,GACVA,EAAa,QAAUqB,EAAoB,QAAQ,SAGrDjB,EAASJ,CAAY,EACrBe,EAAuBf,CAAY,EACnCqB,EAAoB,QAAUrB,EAElC,EAAG,CAACA,EAAce,CAAsB,CAAC,EAGzCF,EAAAA,UAAU,IAAM,CACd,MAAMS,EACJvB,EAAQ,UAAU,OAASS,EAAiB,QAAQ,KAChDe,EACJ,UAAWxB,EAAQ,WACnB,UAAWS,EAAiB,SAC3BT,EAAQ,UAA6B,QACnCS,EAAiB,QAA2B,OAE7Cc,GAAeC,KACjBhB,EAAaR,EAAQ,SAAS,EAC9BS,EAAiB,QAAU,CAAE,GAAGT,EAAQ,SAAA,EAE5C,EAAG,CAACA,EAAQ,UAAU,KAAOA,EAAQ,UAA6B,KAAK,CAAC,EAGxE,MAAMyB,EAAkBR,EAAAA,YACtB,MAAOS,GAA2B,CAChC,GAAI,CACF,MAAMvB,EAAQ,KAAKR,EAAS,CAAE,MAAO+B,EAAU,CACjD,OAAS7B,EAAO,CACd,QAAQ,MACN,wCAAwCF,CAAO,IAC/CE,CAAA,CAEJ,CACF,EACA,CAACF,EAASQ,CAAO,CAAA,EAIbwB,EAAoBV,EAAAA,YACvBS,GAA2B,CAC1B,MAAME,EAAiB,CAAE,GAAGF,CAAA,EAC5B,IAAIG,EAAgB,GAEpB,GAAI,SAAUH,EAAU,CACtB,MAAMI,EAAgB,GAASvB,GAAA,MAAAA,EAA8B,MACvDwB,EAAWL,EAAS,KAE1B,GAAII,EAAe,CACjB,MAAME,EAAWb,EAAgB,MAAM,EACnCa,IAAa,QAAaA,EAAW,EACvCJ,EAAe,KAAOI,EAEtBH,EAAgB,EAEpB,MAAWE,IAAa,IACtBF,EAAgB,GAEpB,CAEA,GAAI3B,IAAc,eAAiB,UAAWwB,EAAU,CACtD,MAAMO,EAAiB,GAAS1B,GAAA,MAAAA,EAA8B,OACxD2B,EAAYR,EAAS,MAE3B,GAAIO,EAAgB,CAClB,MAAMD,EAAWb,EAAgB,OAAO,EACpCa,IAAa,QAAaA,EAAW,EACvCJ,EAAe,MAAQI,EAEvBH,EAAgB,EAEpB,MAAWK,IAAc,IACvBL,EAAgB,GAEpB,CAUA,GARAxB,EAASuB,CAAc,EACvBZ,EAAuBY,CAAc,EAEjChB,EAAe,UACjB,aAAaA,EAAe,OAAO,EACnCA,EAAe,QAAU,MAGvB,CAACiB,EAAe,CAClBhB,EAAuB,QAAU,KACjC,MACF,CAEAA,EAAuB,QAAUe,EACjChB,EAAe,QAAU,WAAW,IAAM,CACxC,MAAMuB,EAAetB,EAAuB,QACxCsB,IACFV,EAAgBU,CAAY,EAC5BtB,EAAuB,QAAU,MAEnCD,EAAe,QAAU,IAC3B,EAAG,GAAG,CACR,EACA,CACEL,EACAY,EACAjB,EACAuB,EACAT,CAAA,CACF,EAIFF,EAAAA,UAAU,IACD,IAAM,CACX,GAAIF,EAAe,QAAS,CAC1B,MAAMuB,EAAetB,EAAuB,QACxCsB,IACFV,EAAgBU,CAAY,EAC5BtB,EAAuB,QAAU,MAEnC,aAAaD,EAAe,OAAO,EACnCA,EAAe,QAAU,IAC3B,CACF,EACC,CAACa,CAAe,CAAC,EAIpB,MAAMW,EAA6BnB,EAAAA,YAAY,SAAY,CAE3D,EAAG,CAAA,CAAE,EAECoB,EAA2BpB,EAAAA,YAAY,SAAY,CAEzD,EAAG,CAAA,CAAE,EAECqB,EAA8BrB,EAAAA,YAAY,SAAY,CAE5D,EAAG,CAAA,CAAE,EAECsB,EAA4BtB,EAAAA,YAAY,SAAY,CAE1D,EAAG,CAAA,CAAE,EAEL,OAAIf,IAAc,cACT,CACL,KAAM,cACN,MAAAE,EACA,UAAAG,EACA,kBAAAoB,EACA,2BAAAS,EACA,yBAAAC,EACA,4BAAAC,EACA,0BAAAC,CAAA,EAGK,CACL,KAAM,YACN,MAAAnC,EACA,UAAAG,EACA,kBAAAoB,EACA,2BAAAS,EACA,yBAAAC,CAAA,CAGN,CCnTO,MAAMG,CAAgE,CAAtE,cACY9C,EAAA,mBAAc,mCACdA,EAAA,yBAAoB,6BAKrC,MAAM,sBAAiE,CACrE,GAAI,CACF,MAAME,EAAS,aAAa,QAAQ,KAAK,WAAW,EACpD,OAAKA,EAGE,KAAK,MAAMA,CAAM,EAFf,CAAA,CAGX,OAASC,EAAO,CACd,eAAQ,MAAM,oCAAqCA,CAAK,EACjD,CAAA,CACT,CACF,CAKA,MAAM,qBACJ4C,EACe,CACf,GAAI,CACF,aAAa,QAAQ,KAAK,YAAa,KAAK,UAAUA,CAAO,CAAC,CAChE,OAAS5C,EAAO,CACd,cAAQ,MAAM,oCAAqCA,CAAK,EAClDA,CACR,CACF,CAKA,MAAM,oBACJ6C,EAC0C,CAC1C,GAAI,CACF,MAAMC,EAAM,GAAG,KAAK,iBAAiB,GAAGD,CAAa,GAC/C9C,EAAS,aAAa,QAAQ+C,CAAG,EACvC,OAAK/C,EAGE,KAAK,MAAMA,CAAM,EAFf,IAGX,OAASC,EAAO,CACd,eAAQ,MACN,uCAAuC6C,CAAa,IACpD7C,CAAA,EAEK,IACT,CACF,CAKA,MAAM,oBACJ6C,EACA5C,EACe,CACf,GAAI,CACF,MAAM6C,EAAM,GAAG,KAAK,iBAAiB,GAAGD,CAAa,GACrD,aAAa,QAAQC,EAAK,KAAK,UAAU7C,CAAK,CAAC,CACjD,OAASD,EAAO,CACd,cAAQ,MACN,uCAAuC6C,CAAa,IACpD7C,CAAA,EAEIA,CACR,CACF,CAKA,MAAM,yBAEJ,CACA,GAAI,CACF,MAAM+C,EAAsD,CAAA,EAG5D,QAASC,EAAI,EAAGA,EAAI,aAAa,OAAQA,IAAK,CAC5C,MAAMF,EAAM,aAAa,IAAIE,CAAC,EAC9B,GAAIF,GAAOA,EAAI,WAAW,KAAK,iBAAiB,EAAG,CACjD,MAAMD,EAAgBC,EAAI,UAAU,KAAK,kBAAkB,MAAM,EAC3D/C,EAAS,aAAa,QAAQ+C,CAAG,EACvC,GAAI/C,EACF,GAAI,CACFgD,EAAUF,CAAa,EAAI,KAAK,MAAM9C,CAAM,CAC9C,OAASkD,EAAY,CACnB,QAAQ,MACN,wCAAwCJ,CAAa,IACrDI,CAAA,CAEJ,CAEJ,CACF,CAEA,OAAOF,CACT,OAAS/C,EAAO,CACd,eAAQ,MAAM,wCAAyCA,CAAK,EACrD,CAAA,CACT,CACF,CACF,CCtGO,MAAMkD,CAAuB,CAOlC,OAAO,WAAW5C,EAAwC,CACxD,KAAK,QAAUA,CACjB,CAKA,aAAa,qBAAgE,CAC3E,MAAM6C,EAAoB,KAAK,2BAAA,EAI/B,MAAO,CACL,GAJqB,MAAM,KAAK,QAAQ,qBAAA,EAKxC,GAAGA,CAAA,CAEP,CAKA,aAAa,mBAAmBC,EAA6C,CAE3E,OADmB,MAAM,KAAK,oBAAA,GACZA,CAAE,GAAK,IAC3B,CAKA,aAAa,sBACXjD,EAC0B,CAC1B,MAAMkD,EAAiB,MAAM,KAAK,QAAQ,qBAAA,EACpCD,EAAK,KAAK,oBAAoBjD,EAAQ,IAAI,EAE1CmD,EAA6B,CACjC,GAAAF,EACA,KAAMjD,EAAQ,KACd,YAAaA,EAAQ,YACrB,OAAQA,EAAQ,OAChB,aAAcA,EAAQ,aACtB,iBAAkBA,EAAQ,iBAC1B,UAAW,KAAK,IAAA,EAChB,UAAW,KAAK,IAAA,EAChB,UAAW,EAAA,EAGPoD,EAAiB,CACrB,GAAGF,EACH,CAACD,CAAE,EAAGE,CAAA,EAGR,aAAM,KAAK,QAAQ,qBAAqBC,CAAc,EAE/CD,CACT,CAKA,aAAa,sBACXF,EACAI,EACiC,CACjC,MAAMH,EAAiB,MAAM,KAAK,QAAQ,qBAAA,EACpCI,EAAoBJ,EAAeD,CAAE,EAE3C,GAAI,CAACK,EACH,eAAQ,MAAM,oBAAoBL,CAAE,YAAY,EACzC,KAIT,GAAIK,EAAkB,UACpB,eAAQ,MAAM,2CAA2CL,CAAE,EAAE,EACtD,KAGT,MAAMM,EAAoC,CACxC,GAAGD,EACH,GAAGD,EACH,GAAAJ,EACA,UAAWK,EAAkB,UAC7B,UAAW,KAAK,IAAA,CAAI,EAGhBF,EAAiB,CACrB,GAAGF,EACH,CAACD,CAAE,EAAGM,CAAA,EAGR,aAAM,KAAK,QAAQ,qBAAqBH,CAAc,EAE/CG,CACT,CAKA,aAAa,sBAAsBN,EAA8B,CAC/D,MAAMC,EAAiB,MAAM,KAAK,QAAQ,qBAAA,EACpCC,EAAYD,EAAeD,CAAE,EAEnC,GAAI,CAACE,EACH,eAAQ,MAAM,oBAAoBF,CAAE,YAAY,EACzC,GAIT,GAAIE,EAAU,UACZ,eAAQ,MAAM,2CAA2CF,CAAE,EAAE,EACtD,GAGT,MAAMG,EAAiB,CAAE,GAAGF,CAAA,EAC5B,cAAOE,EAAeH,CAAE,EAExB,MAAM,KAAK,QAAQ,qBAAqBG,CAAc,EAE/C,EACT,CAKA,aAAa,mBACXV,EAC0C,CAC1C,OAAO,MAAM,KAAK,QAAQ,oBAAoBA,CAAa,CAC7D,CAKA,aAAa,mBACXA,EACA5C,EACe,CACf,MAAM,KAAK,QAAQ,oBAAoB4C,EAAe5C,CAAK,CAC7D,CAKA,aAAa,sBACX4C,EACAtC,EACe,CACf,MAAMoD,EAAe,MAAM,KAAK,mBAAmBd,CAAa,EAC3Dc,GAEL,MAAM,KAAK,mBAAmBd,EAAe,CAC3C,GAAGc,EACH,MAAApD,CAAA,CACD,CACH,CAKA,aAAa,0BACXsC,EACAnC,EACe,CACf,MAAMiD,EAAe,MAAM,KAAK,mBAAmBd,CAAa,EAC3Dc,GAEL,MAAM,KAAK,mBAAmBd,EAAe,CAC3C,GAAGc,EACH,UAAAjD,CAAA,CACD,CACH,CAKA,OAAO,kBACLkD,EAKAN,EAIA,CACA,MAAMO,EAAmBP,EAAU,aAC/B,KAAK,UAAUM,EAAU,KAAK,IAC9B,KAAK,UAAUN,EAAU,YAAY,EACrC,GAEEQ,EAAwBR,EAAU,iBACpC,KAAK,UAAUM,EAAU,SAAS,IAClC,KAAK,UAAUN,EAAU,gBAAgB,EACzC,GAEJ,MAAO,CAAE,iBAAAO,EAAkB,sBAAAC,CAAA,CAC7B,CAKA,aAAa,mCACXC,EACAlB,EACe,CACf,MAAMS,EAAY,MAAM,KAAK,mBAAmBS,CAAW,EACrDH,EAAY,MAAM,KAAK,mBAAmBf,CAAa,EAE7D,GAAI,CAACS,GAAa,CAACM,GAAaN,EAAU,UAAW,CACnD,QAAQ,MACN,+DAAA,EAEF,MACF,CAEA,MAAM,KAAK,sBAAsBS,EAAa,CAC5C,aAAcH,EAAU,MACxB,iBAAkBA,EAAU,SAAA,CAC7B,CACH,CAKA,aAAa,mCACXf,EACAkB,EACe,CACf,MAAMT,EAAY,MAAM,KAAK,mBAAmBS,CAAW,EAC3D,GAAI,CAACT,EAAW,CACd,QAAQ,MAAM,aAAaS,CAAW,YAAY,EAClD,MACF,CAEA,MAAMJ,EAAe,MAAM,KAAK,mBAAmBd,CAAa,EAC3Dc,GAEL,MAAM,KAAK,mBAAmBd,EAAe,CAC3C,GAAGc,EACH,MAAOL,EAAU,cAAgB,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAChE,UAAWA,EAAU,kBAAoB,CAAE,KAAM,GAAO,MAAO,EAAA,CAAM,CACtE,CACH,CAKA,OAAO,0BACLU,EACAV,EACS,CACT,OAAO,KAAK,gBAAgBU,EAAQV,EAAU,MAAM,CACtD,CAKA,aAAa,sBACXU,EACwB,CACxB,MAAMC,EAAa,MAAM,KAAK,oBAAA,EAE9B,SAAW,CAACb,EAAIE,CAAS,IAAK,OAAO,QAAQW,CAAU,EACrD,GAAI,KAAK,0BAA0BD,EAAQV,CAAS,EAClD,OAAOF,EAIX,OAAO,IACT,CAKA,OAAe,gBACbc,EACAC,EACS,CACT,OAAO,KAAK,UAAUD,CAAO,IAAM,KAAK,UAAUC,CAAO,CAC3D,CAKA,OAAe,oBAAoBC,EAAsB,CAEvD,MAAO,GADMA,EAAK,YAAA,EAAc,QAAQ,cAAe,GAAG,CAC5C,IAAI,KAAK,KAAK,EAC9B,CAKA,OAAO,4BAA8D,CACnE,MAAMC,EAAM,KAAK,IAAA,EAEjB,MAAO,CACL,qBAAsB,CACpB,GAAI,qBACJ,KAAM,qBACN,YACE,2IACF,OAAQ,CACN,KAAM,CACJ,KAAM,OACN,OAAQ,CACN,QACA,eACA,YACA,WACA,OACA,UAAA,EAEF,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,OAAQ,gBACR,MAAO,CACL,KAAM,OACN,OAAQ,CACN,oBACA,aACA,iBACA,mBAAA,EAEF,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,CACpD,EAEF,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,cAAe,CACb,GAAI,cACJ,KAAM,cACN,YACE,2GACF,OAAQ,CACN,KAAM,CACJ,KAAM,OACN,OAAQ,CAAC,aAAc,kBAAmB,UAAU,EACpD,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,OAAQ,CACN,KAAM,OACN,OAAQ,CAAC,UAAW,YAAY,EAChC,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,MAAO,mBAAA,EAET,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,cAAe,CACb,GAAI,gBACJ,KAAM,gBACN,YAAa,yCACb,OAAQ,CACN,KAAM,OACN,OAAQ,iBACR,MAAO,YAAA,EAET,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,aAAc,CACZ,GAAI,aACJ,KAAM,aACN,YACE,+IACF,OAAQ,CACN,KAAM,CACJ,KAAM,OACN,OAAQ,CACN,QACA,gBACA,WACA,eACA,aACA,MAAA,EAEF,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,OAAQ,gBACR,MAAO,CACL,KAAM,OACN,OAAQ,CACN,oBACA,cACA,aACA,gBAAA,EAEF,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,CACpD,EAEF,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,gBAAiB,CACf,GAAI,gBACJ,KAAM,gBACN,YACE,8IACF,OAAQ,CACN,KAAM,CACJ,KAAM,OACN,OAAQ,CAAC,cAAe,QAAS,cAAc,EAC/C,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,OAAQ,oBACR,MAAO,CACL,KAAM,OACN,OAAQ,CAAC,gBAAiB,YAAY,EACtC,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,CACpD,EAEF,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,QAAS,CACP,GAAI,UACJ,KAAM,UACN,YACE,4EACF,OAAQ,CACN,KAAM,CACJ,KAAM,OACN,OAAQ,CAAC,WAAY,MAAM,EAC3B,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,OAAQ,oBACR,MAAO,CACL,KAAM,OACN,OAAQ,CAAC,gBAAiB,gBAAgB,EAC1C,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,CACpD,EAEF,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,aAAc,CACZ,GAAI,aACJ,KAAM,aACN,YACE,gJACF,OAAQ,CACN,KAAM,CACJ,KAAM,OACN,OAAQ,CAAC,WAAY,SAAU,aAAc,MAAM,EACnD,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,OAAQ,CACN,KAAM,OACN,OAAQ,CAAC,aAAc,gBAAgB,EACvC,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,EAEpD,MAAO,CACL,KAAM,OACN,OAAQ,CAAC,gBAAiB,mBAAmB,EAC7C,OAAQ,CAAE,iBAAkB,EAAG,YAAa,KAAA,CAAM,CACpD,EAEF,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,EAEb,mBAAoB,CAClB,GAAI,mBACJ,KAAM,mBACN,YACE,yEACF,OAAQ,CACN,KAAM,OACN,OAAQ,YACR,MAAO,eAAA,EAET,aAAc,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAC7C,iBAAkB,CAAE,KAAM,GAAO,MAAO,EAAA,EACxC,UAAWA,EACX,UAAWA,EACX,UAAW,EAAA,CACb,CAEJ,CAKA,aAAa,4BAA4C,CACvD,MAAMhB,EAAiB,MAAM,KAAK,QAAQ,qBAAA,EAItC,OAAO,KAAKA,CAAc,EAAE,MAIlC,CACF,CA3gBExD,EADWqD,EACI,UACb,IAAIP,GC8FD,SAAS2B,EACdnE,EAA+B,GACX,CACpB,KAAM,CAAE,cAAA0C,EAAe,eAAA0B,EAAiB,GAAO,mBAAAC,GAAuBrE,EAEhE,CAAC8D,EAAYQ,CAAa,EAAIhE,EAAAA,SAClC,CAAA,CAAC,EAEG,CAACiE,EAAiBC,CAAkB,EACxClE,EAAAA,SAA0C,IAAI,EAC1C,CAACmE,EAAiBC,CAAkB,EACxCpE,EAAAA,SAAiC,IAAI,EACjC,CAACqE,EAASC,CAAU,EAAItE,EAAAA,SAAS,EAAI,EACrC,CAACT,EAAOgF,CAAQ,EAAIvE,EAAAA,SAAuB,IAAI,EAK/CwE,EAAW7D,EAAAA,YAAY,SAAY,CACvC,GAAI,CACF2D,EAAW,EAAI,EACfC,EAAS,IAAI,EAGb,MAAME,EACJ,MAAMhC,EAAuB,oBAAA,EAI/B,GAHAuB,EAAcS,CAAa,EAGvBrC,EAAe,CACjB,IAAIe,EACF,MAAMV,EAAuB,mBAAmBL,CAAa,EAG/D,GAAI,CAACe,GAAaW,EAAgB,CAChC,MAAMR,EACJS,GAAsB,qBAClBlB,EACJ,MAAMJ,EAAuB,mBAAmBa,CAAW,EAEzDT,IACFM,EAAY,CACV,YAAAG,EACA,MAAOT,EAAU,cAAgB,CAC/B,KAAM,GACN,OAAQ,GACR,MAAO,EAAA,EAET,UAAWA,EAAU,kBAAoB,CACvC,KAAM,GACN,MAAO,EAAA,CACT,EAGF,MAAMJ,EAAuB,mBAC3BL,EACAe,CAAA,EAGN,CAKA,GAHAe,EAAmBf,CAAS,EAGxBA,GAAA,MAAAA,EAAW,YAAa,CAC1B,MAAMN,EAAY,MAAMJ,EAAuB,mBAC7CU,EAAU,WAAA,EAEZiB,EAAmBvB,CAAS,CAC9B,MACEuB,EAAmB,IAAI,CAE3B,CACF,OAASM,EAAK,CACZH,EAASG,CAAY,EACrB,QAAQ,MAAM,iCAAkCA,CAAG,CACrD,QAAA,CACEJ,EAAW,EAAK,CAClB,CACF,EAAG,CAAClC,EAAe0B,EAAgBC,CAAkB,CAAC,EAKtDvD,EAAAA,UAAU,IAAM,CACdgE,EAAA,CACF,EAAG,CAACA,CAAQ,CAAC,EAKb,MAAMG,EAAehE,EAAAA,YACnB,MAAOgC,GACE,MAAMF,EAAuB,mBAAmBE,CAAE,EAE3D,CAAA,CAAC,EAMGiC,EAAkBjE,EAAAA,YACtB,MAAOkE,GAA0C,CAC/C,MAAMhC,EACJ,MAAMJ,EAAuB,sBAAsBoC,CAAa,EAClE,aAAML,EAAA,EACC3B,CACT,EACA,CAAC2B,CAAQ,CAAA,EAMLM,EAAkBnE,EAAAA,YACtB,MAAOgC,EAAYI,IAAoC,CACrD,MAAMF,EACJ,MAAMJ,EAAuB,sBAAsBE,EAAII,CAAO,EAChE,aAAMyB,EAAA,EACC3B,CACT,EACA,CAAC2B,CAAQ,CAAA,EAMLO,EAAkBpE,EAAAA,YACtB,MAAOgC,GAAe,CACpB,MAAMqC,EAAU,MAAMvC,EAAuB,sBAAsBE,CAAE,EACrE,OAAIqC,GACF,MAAMR,EAAA,EAEDQ,CACT,EACA,CAACR,CAAQ,CAAA,EAMLS,EAAiBtE,EAAAA,YACrB,MAAO2C,GAAwB,CAC7B,GAAI,CAAClB,EAAe,CAClB,QAAQ,MAAM,8CAA8C,EAC5D,MACF,CAEA,MAAMS,EACJ,MAAMJ,EAAuB,mBAAmBa,CAAW,EAC7D,GAAI,CAACT,EAAW,CACd,QAAQ,MAAM,aAAaS,CAAW,YAAY,EAClD,MACF,CAEA,MAAM4B,EAAqC,CACzC,YAAA5B,EACA,MAAOT,EAAU,cAAgB,CAAE,KAAM,GAAI,OAAQ,GAAI,MAAO,EAAA,EAChE,UAAWA,EAAU,kBAAoB,CACvC,KAAM,GACN,MAAO,EAAA,CACT,EAGF,MAAMJ,EAAuB,mBAAmBL,EAAe8C,CAAQ,EACvE,MAAMV,EAAA,CACR,EACA,CAACpC,EAAeoC,CAAQ,CAAA,EAMpBW,EAAcxE,EAAAA,YAClB,MAAOb,GAA2D,CAChE,GAAI,CAACsC,EAAe,CAClB,QAAQ,MAAM,2CAA2C,EACzD,MACF,CAEA,MAAMK,EAAuB,sBAAsBL,EAAetC,CAAK,EACvE,MAAM0E,EAAA,CACR,EACA,CAACpC,EAAeoC,CAAQ,CAAA,EAMpBY,EAAkBzE,EAAAA,YACtB,MAAOV,GAAmD,CACxD,GAAI,CAACmC,EAAe,CAClB,QAAQ,MAAM,+CAA+C,EAC7D,MACF,CAEA,MAAMK,EAAuB,0BAC3BL,EACAnC,CAAA,EAEF,MAAMuE,EAAA,CACR,EACA,CAACpC,EAAeoC,CAAQ,CAAA,EAMpBa,EAAkB1E,EAAAA,YAAY,SAAY,CAC9C,GAAI,CAACyB,GAAiB,EAAC6B,GAAA,MAAAA,EAAiB,aAAa,CACnD,QAAQ,MAAM,yDAAyD,EACvE,MACF,CAEA,MAAMxB,EAAuB,mCAC3BL,EACA6B,EAAgB,WAAA,EAElB,MAAMO,EAAA,CACR,EAAG,CAACpC,EAAe6B,GAAA,YAAAA,EAAiB,YAAaO,CAAQ,CAAC,EAKpDc,EAAU3E,EAAAA,YAAY,SAAY,CACtC,MAAM6D,EAAA,CACR,EAAG,CAACA,CAAQ,CAAC,EAEb,MAAO,CACL,WAAAhB,EACA,gBAAAS,EACA,gBAAAE,EACA,QAAAE,EACA,MAAA9E,EACA,aAAAoF,EACA,gBAAAC,EACA,gBAAAE,EACA,gBAAAC,EACA,eAAAE,EACA,YAAAE,EACA,gBAAAC,EACA,gBAAAC,EACA,QAAAC,CAAA,CAEJ"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/hooks/usePanelPersistence.ts","../src/services/LocalStorageWorkspaceAdapter.ts","../src/services/WorkspaceLayoutService.ts","../src/hooks/useWorkspace.ts"],"sourcesContent":["/**\n * usePanelPersistence Hook\n *\n * Extracted and adapted from electron-app/src/renderer/hooks/usePanelPersistence.ts\n * Manages saving and loading panel sizes and collapsed states with debounced persistence.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type {\n UsePanelPersistenceOptions,\n PanelSizes,\n TwoPanelSizes,\n PanelCollapsed,\n PanelPersistence,\n PersistenceAdapter,\n} from '../types/persistence.types';\n\n/**\n * Default localStorage-based persistence adapter for web applications\n */\nexport class LocalStoragePersistenceAdapter implements PersistenceAdapter {\n private storageKey = 'panel-layouts';\n\n async load(viewKey: string) {\n try {\n const stored = localStorage.getItem(`${this.storageKey}:${viewKey}`);\n return stored ? JSON.parse(stored) : null;\n } catch (error) {\n console.error(`Failed to load panel layout for ${viewKey}:`, error);\n return null;\n }\n }\n\n async save(viewKey: string, state: { sizes: PanelSizes | TwoPanelSizes }) {\n try {\n localStorage.setItem(\n `${this.storageKey}:${viewKey}`,\n JSON.stringify(state),\n );\n } catch (error) {\n console.error(`Failed to save panel layout for ${viewKey}:`, error);\n }\n }\n}\n\n/**\n * Hook for persisting panel layouts across sessions\n *\n * Manages panel sizes and collapsed states with automatic debounced saving.\n * Supports both 2-panel and 3-panel layouts.\n *\n * @param options - Configuration options for persistence\n * @returns Panel state and handlers for resizing and collapsing\n *\n * @example\n * ```tsx\n * const persistence = usePanelPersistence({\n * viewKey: 'my-app',\n * defaultSizes: { left: 30, middle: 70, right: 0 },\n * collapsed: { left: false, right: true },\n * panelType: 'three-panel',\n * });\n *\n * <ConfigurablePanelLayout\n * {...otherProps}\n * defaultSizes={persistence.sizes}\n * collapsed={persistence.collapsed}\n * onPanelResize={persistence.handlePanelResize}\n * />\n * ```\n */\nexport function usePanelPersistence(\n options: UsePanelPersistenceOptions,\n): PanelPersistence {\n const {\n viewKey,\n defaultSizes,\n panelType,\n adapter = new LocalStoragePersistenceAdapter(),\n } = options;\n\n const [sizes, setSizes] = useState(defaultSizes);\n const [collapsed, setCollapsed] = useState(options.collapsed);\n const prevCollapsedRef = useRef(options.collapsed);\n const lastNonZeroSizesRef = useRef<Partial<PanelSizes & TwoPanelSizes>>({});\n const saveTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingPersistSizesRef = useRef<typeof sizes | null>(null);\n\n // Load saved state on mount\n useEffect(() => {\n const loadSavedState = async () => {\n const saved = await adapter.load(viewKey);\n if (saved?.sizes) {\n setSizes(saved.sizes as typeof sizes);\n updateLastNonZeroSizes(saved.sizes);\n }\n };\n loadSavedState();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [viewKey]);\n\n const updateLastNonZeroSizes = useCallback(\n (incomingSizes: PanelSizes | TwoPanelSizes) => {\n if ('left' in incomingSizes && incomingSizes.left > 0) {\n lastNonZeroSizesRef.current.left = incomingSizes.left;\n }\n\n if ('middle' in incomingSizes && incomingSizes.middle > 0) {\n lastNonZeroSizesRef.current.middle = incomingSizes.middle;\n }\n\n if ('right' in incomingSizes && incomingSizes.right > 0) {\n lastNonZeroSizesRef.current.right = incomingSizes.right;\n }\n },\n [],\n );\n\n const getFallbackSize = useCallback(\n (panel: 'left' | 'right') => {\n const storedSize = lastNonZeroSizesRef.current[panel];\n if (storedSize && storedSize > 0) {\n return storedSize;\n }\n\n if (panel === 'left' && 'left' in defaultSizes && defaultSizes.left > 0) {\n return defaultSizes.left;\n }\n\n if (\n panel === 'right' &&\n 'right' in defaultSizes &&\n defaultSizes.right > 0\n ) {\n return defaultSizes.right;\n }\n\n return undefined;\n },\n [defaultSizes],\n );\n\n // Update sizes when defaultSizes changes (parent has loaded preferences)\n const prevDefaultSizesRef = useRef(defaultSizes);\n useEffect(() => {\n // Only update if the actual values changed, not just the reference\n const hasChanged =\n ('left' in defaultSizes &&\n defaultSizes.left !== prevDefaultSizesRef.current.left) ||\n ('middle' in defaultSizes &&\n 'middle' in prevDefaultSizesRef.current &&\n defaultSizes.middle !==\n (prevDefaultSizesRef.current as PanelSizes).middle) ||\n ('right' in defaultSizes &&\n defaultSizes.right !== prevDefaultSizesRef.current.right);\n\n if (hasChanged) {\n setSizes(defaultSizes);\n updateLastNonZeroSizes(defaultSizes);\n prevDefaultSizesRef.current = defaultSizes;\n }\n }, [defaultSizes, updateLastNonZeroSizes]);\n\n // Sync with parent's collapsed state (e.g., from titlebar buttons)\n useEffect(() => {\n const leftChanged =\n options.collapsed.left !== prevCollapsedRef.current.left;\n const rightChanged =\n 'right' in options.collapsed &&\n 'right' in prevCollapsedRef.current &&\n (options.collapsed as PanelCollapsed).right !==\n (prevCollapsedRef.current as PanelCollapsed).right;\n\n if (leftChanged || rightChanged) {\n setCollapsed(options.collapsed);\n prevCollapsedRef.current = { ...options.collapsed };\n }\n }, [options.collapsed.left, (options.collapsed as PanelCollapsed).right]);\n\n // Save preferences helper (only saves sizes, not collapsed state)\n const savePreferences = useCallback(\n async (newSizes: typeof sizes) => {\n try {\n await adapter.save(viewKey, { sizes: newSizes });\n } catch (error) {\n console.error(\n `Failed to save panel preferences for ${viewKey}:`,\n error,\n );\n }\n },\n [viewKey, adapter],\n );\n\n // Handle panel resize (debounced)\n const handlePanelResize = useCallback(\n (newSizes: typeof sizes) => {\n const sanitizedSizes = { ...newSizes } as typeof newSizes;\n let shouldPersist = true;\n\n if ('left' in newSizes) {\n const leftCollapsed = Boolean((collapsed as PanelCollapsed)?.left);\n const leftSize = newSizes.left;\n\n if (leftCollapsed) {\n const fallback = getFallbackSize('left');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.left = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (leftSize === 0) {\n shouldPersist = false;\n }\n }\n\n if (panelType === 'three-panel' && 'right' in newSizes) {\n const rightCollapsed = Boolean((collapsed as PanelCollapsed)?.right);\n const rightSize = newSizes.right;\n\n if (rightCollapsed) {\n const fallback = getFallbackSize('right');\n if (fallback !== undefined && fallback > 0) {\n sanitizedSizes.right = fallback;\n } else {\n shouldPersist = false;\n }\n } else if (rightSize === 0) {\n shouldPersist = false;\n }\n }\n\n setSizes(sanitizedSizes);\n updateLastNonZeroSizes(sanitizedSizes);\n\n if (saveTimeoutRef.current) {\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n\n if (!shouldPersist) {\n pendingPersistSizesRef.current = null;\n return;\n }\n\n pendingPersistSizesRef.current = sanitizedSizes;\n saveTimeoutRef.current = setTimeout(() => {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n saveTimeoutRef.current = null;\n }, 500);\n },\n [\n collapsed,\n getFallbackSize,\n panelType,\n savePreferences,\n updateLastNonZeroSizes,\n ],\n );\n\n // Cleanup: save any pending changes on unmount\n useEffect(() => {\n return () => {\n if (saveTimeoutRef.current) {\n const pendingSizes = pendingPersistSizesRef.current;\n if (pendingSizes) {\n savePreferences(pendingSizes);\n pendingPersistSizesRef.current = null;\n }\n clearTimeout(saveTimeoutRef.current);\n saveTimeoutRef.current = null;\n }\n };\n }, [savePreferences]);\n\n // Collapse/expand handlers - no-ops because state is controlled by parent\n // The parent manages collapsed state via titlebar buttons and passes it down as props\n const handleLeftCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleLeftExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightCollapseComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n const handleRightExpandComplete = useCallback(async () => {\n // No-op: parent controls state\n }, []);\n\n if (panelType === 'three-panel') {\n return {\n type: 'three-panel',\n sizes: sizes as PanelSizes,\n collapsed: collapsed as PanelCollapsed,\n handlePanelResize: handlePanelResize as (sizes: PanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n handleRightCollapseComplete,\n handleRightExpandComplete,\n };\n } else {\n return {\n type: 'two-panel',\n sizes: sizes as TwoPanelSizes,\n collapsed: collapsed as { left?: boolean },\n handlePanelResize: handlePanelResize as (sizes: TwoPanelSizes) => void,\n handleLeftCollapseComplete,\n handleLeftExpandComplete,\n };\n }\n}\n","/**\n * LocalStorageWorkspaceAdapter - Browser localStorage implementation\n * Stores workspace presets and repository states in browser localStorage\n */\n\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n} from '../types/workspace.types';\n\nexport class LocalStorageWorkspaceAdapter implements WorkspaceStorageAdapter {\n private readonly PRESETS_KEY = 'panel-layouts:workspace-presets';\n private readonly REPO_STATE_PREFIX = 'panel-layouts:repo-state:';\n\n /**\n * Load all user-created workspace presets\n */\n async loadWorkspacePresets(): Promise<Record<string, WorkspaceLayout>> {\n try {\n const stored = localStorage.getItem(this.PRESETS_KEY);\n if (!stored) {\n return {};\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error('Failed to load workspace presets:', error);\n return {};\n }\n }\n\n /**\n * Save workspace presets\n */\n async saveWorkspacePresets(\n presets: Record<string, WorkspaceLayout>,\n ): Promise<void> {\n try {\n localStorage.setItem(this.PRESETS_KEY, JSON.stringify(presets));\n } catch (error) {\n console.error('Failed to save workspace presets:', error);\n throw error;\n }\n }\n\n /**\n * Load repository-specific workspace state\n */\n async loadRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n const stored = localStorage.getItem(key);\n if (!stored) {\n return null;\n }\n return JSON.parse(stored);\n } catch (error) {\n console.error(\n `Failed to load repository state for ${repositoryKey}:`,\n error,\n );\n return null;\n }\n }\n\n /**\n * Save repository-specific workspace state\n */\n async saveRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n try {\n const key = `${this.REPO_STATE_PREFIX}${repositoryKey}`;\n localStorage.setItem(key, JSON.stringify(state));\n } catch (error) {\n console.error(\n `Failed to save repository state for ${repositoryKey}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Load all repository states\n */\n async loadAllRepositoryStates(): Promise<\n Record<string, RepositoryWorkspaceState>\n > {\n try {\n const allStates: Record<string, RepositoryWorkspaceState> = {};\n\n // Iterate through all localStorage keys\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n if (key && key.startsWith(this.REPO_STATE_PREFIX)) {\n const repositoryKey = key.substring(this.REPO_STATE_PREFIX.length);\n const stored = localStorage.getItem(key);\n if (stored) {\n try {\n allStates[repositoryKey] = JSON.parse(stored);\n } catch (parseError) {\n console.error(\n `Failed to parse repository state for ${repositoryKey}:`,\n parseError,\n );\n }\n }\n }\n }\n\n return allStates;\n } catch (error) {\n console.error('Failed to load all repository states:', error);\n return {};\n }\n }\n}\n","/**\n * WorkspaceLayoutService - Manages workspace layout presets\n * Extracted from electron-app/src/renderer/services/WorkspaceLayoutService.ts\n *\n * This service handles CRUD operations for workspace layouts (panel configuration presets)\n * and provides utilities for comparing layouts and managing built-in workspaces.\n */\n\nimport type { PanelLayout } from '@principal-ade/panels';\nimport type {\n WorkspaceLayout,\n WorkspaceStorageAdapter,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\nimport { LocalStorageWorkspaceAdapter } from './LocalStorageWorkspaceAdapter';\n\nexport class WorkspaceLayoutService {\n private static adapter: WorkspaceStorageAdapter =\n new LocalStorageWorkspaceAdapter();\n\n /**\n * Configure a custom storage adapter (for Electron IPC or remote storage)\n */\n static setAdapter(adapter: WorkspaceStorageAdapter): void {\n this.adapter = adapter;\n }\n\n /**\n * Get all workspace layouts (including built-in)\n */\n static async getWorkspaceLayouts(): Promise<Record<string, WorkspaceLayout>> {\n const builtInWorkspaces = this.getBuiltInWorkspaceLayouts();\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Merge built-in and user workspaces, built-in takes precedence\n return {\n ...userWorkspaces,\n ...builtInWorkspaces,\n };\n }\n\n /**\n * Get a specific workspace layout by ID\n */\n static async getWorkspaceLayout(id: string): Promise<WorkspaceLayout | null> {\n const workspaces = await this.getWorkspaceLayouts();\n return workspaces[id] || null;\n }\n\n /**\n * Create a new workspace layout\n */\n static async createWorkspaceLayout(\n options: CreateWorkspaceOptions,\n ): Promise<WorkspaceLayout> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const id = this.generateWorkspaceId(options.name);\n\n const workspace: WorkspaceLayout = {\n id,\n name: options.name,\n description: options.description,\n layout: options.layout,\n defaultSizes: options.defaultSizes,\n defaultCollapsed: options.defaultCollapsed,\n createdAt: Date.now(),\n updatedAt: Date.now(),\n isBuiltIn: false,\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: workspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return workspace;\n }\n\n /**\n * Update an existing workspace layout\n */\n static async updateWorkspaceLayout(\n id: string,\n updates: UpdateWorkspaceOptions,\n ): Promise<WorkspaceLayout | null> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const existingWorkspace = userWorkspaces[id];\n\n if (!existingWorkspace) {\n console.error(`Workspace layout ${id} not found`);\n return null;\n }\n\n // Don't allow updating built-in workspaces\n if (existingWorkspace.isBuiltIn) {\n console.error(`Cannot update built-in workspace layout ${id}`);\n return null;\n }\n\n const updatedWorkspace: WorkspaceLayout = {\n ...existingWorkspace,\n ...updates,\n id, // Preserve ID\n createdAt: existingWorkspace.createdAt, // Preserve creation time\n updatedAt: Date.now(),\n };\n\n const updatedPresets = {\n ...userWorkspaces,\n [id]: updatedWorkspace,\n };\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return updatedWorkspace;\n }\n\n /**\n * Delete a workspace layout\n */\n static async deleteWorkspaceLayout(id: string): Promise<boolean> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n const workspace = userWorkspaces[id];\n\n if (!workspace) {\n console.error(`Workspace layout ${id} not found`);\n return false;\n }\n\n // Don't allow deleting built-in workspaces\n if (workspace.isBuiltIn) {\n console.error(`Cannot delete built-in workspace layout ${id}`);\n return false;\n }\n\n const updatedPresets = { ...userWorkspaces };\n delete updatedPresets[id];\n\n await this.adapter.saveWorkspacePresets(updatedPresets);\n\n return true;\n }\n\n /**\n * Get repository state (which workspace + current sizes/collapsed)\n */\n static async getRepositoryState(\n repositoryKey: string,\n ): Promise<RepositoryWorkspaceState | null> {\n return await this.adapter.loadRepositoryState(repositoryKey);\n }\n\n /**\n * Set repository state (which workspace + current sizes/collapsed/layout)\n */\n static async setRepositoryState(\n repositoryKey: string,\n state: RepositoryWorkspaceState,\n ): Promise<void> {\n await this.adapter.saveRepositoryState(repositoryKey, state);\n }\n\n /**\n * Update only sizes in repository state\n */\n static async updateRepositorySizes(\n repositoryKey: string,\n sizes: { left: number; middle: number; right: number },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes,\n });\n }\n\n /**\n * Update only collapsed state in repository state\n */\n static async updateRepositoryCollapsed(\n repositoryKey: string,\n collapsed: { left?: boolean; right?: boolean },\n ): Promise<void> {\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n collapsed,\n });\n }\n\n /**\n * Check if repository state differs from workspace defaults\n */\n static hasStateDeviation(\n repoState: {\n workspaceId: string | null;\n sizes: { left: number; middle: number; right: number };\n collapsed: { left?: boolean; right?: boolean };\n },\n workspace: WorkspaceLayout,\n ): {\n hasSizeDeviation: boolean;\n hasCollapsedDeviation: boolean;\n } {\n const hasSizeDeviation = workspace.defaultSizes\n ? JSON.stringify(repoState.sizes) !==\n JSON.stringify(workspace.defaultSizes)\n : false;\n\n const hasCollapsedDeviation = workspace.defaultCollapsed\n ? JSON.stringify(repoState.collapsed) !==\n JSON.stringify(workspace.defaultCollapsed)\n : false;\n\n return { hasSizeDeviation, hasCollapsedDeviation };\n }\n\n /**\n * Update workspace defaults from repository state\n */\n static async updateWorkspaceFromRepositoryState(\n workspaceId: string,\n repositoryKey: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n const repoState = await this.getRepositoryState(repositoryKey);\n\n if (!workspace || !repoState || workspace.isBuiltIn) {\n console.error(\n 'Cannot update built-in workspace or workspace/state not found',\n );\n return;\n }\n\n await this.updateWorkspaceLayout(workspaceId, {\n defaultSizes: repoState.sizes,\n defaultCollapsed: repoState.collapsed,\n });\n }\n\n /**\n * Reset repository state to workspace defaults\n */\n static async resetRepositoryToWorkspaceDefaults(\n repositoryKey: string,\n workspaceId: string,\n ): Promise<void> {\n const workspace = await this.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const currentState = await this.getRepositoryState(repositoryKey);\n if (!currentState) return;\n\n await this.setRepositoryState(repositoryKey, {\n ...currentState,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || { left: false, right: false },\n });\n }\n\n /**\n * Check if a layout matches a workspace layout\n */\n static isLayoutMatchingWorkspace(\n layout: PanelLayout,\n workspace: WorkspaceLayout,\n ): boolean {\n return this.areLayoutsEqual(layout, workspace.layout);\n }\n\n /**\n * Find workspace ID that matches the given layout\n */\n static async findMatchingWorkspace(\n layout: PanelLayout,\n ): Promise<string | null> {\n const workspaces = await this.getWorkspaceLayouts();\n\n for (const [id, workspace] of Object.entries(workspaces)) {\n if (this.isLayoutMatchingWorkspace(layout, workspace)) {\n return id;\n }\n }\n\n return null;\n }\n\n /**\n * Deep comparison of two panel layouts\n */\n private static areLayoutsEqual(\n layout1: PanelLayout,\n layout2: PanelLayout,\n ): boolean {\n return JSON.stringify(layout1) === JSON.stringify(layout2);\n }\n\n /**\n * Generate a unique ID from a workspace name\n */\n private static generateWorkspaceId(name: string): string {\n const base = name.toLowerCase().replace(/[^a-z0-9]+/g, '-');\n return `${base}-${Date.now()}`;\n }\n\n /**\n * Get built-in workspace layouts\n */\n static getBuiltInWorkspaceLayouts(): Record<string, WorkspaceLayout> {\n const now = Date.now();\n\n return {\n 'project-management': {\n id: 'project-management',\n name: 'Project Management',\n description:\n 'Tasks, dependencies, issues, file tree, docs, drawings, multi terminal, city visualization, code viewer, markdown slides, and excalidraw',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'dependencies',\n 'gitIssues',\n 'fileTree',\n 'docs',\n 'drawings',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'codeViewer',\n 'markdownViewer',\n 'excalidrawDiagram',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'code-review': {\n id: 'code-review',\n name: 'Code Review',\n description:\n 'Git changes, pull requests, and file tree on left, git diff and code viewer in middle, city map on right',\n layout: {\n left: {\n type: 'tabs',\n panels: ['gitChanges', 'gitPullRequests', 'fileTree'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['gitDiff', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: 'cityVisualization',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n documentation: {\n id: 'documentation',\n name: 'Documentation',\n description: 'Docs, markdown viewer, and code viewer',\n layout: {\n left: 'docs',\n middle: 'markdownViewer',\n right: 'codeViewer',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'agent-work': {\n id: 'agent-work',\n name: 'Agent Work',\n description:\n 'Tasks, agent sessions, file tree, agent context, git changes, docs, multi terminal, city map, agent events, code viewer, and markdown slides',\n layout: {\n left: {\n type: 'tabs',\n panels: [\n 'tasks',\n 'agentSessions',\n 'fileTree',\n 'agentContext',\n 'gitChanges',\n 'docs',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'multiTerminal',\n right: {\n type: 'tabs',\n panels: [\n 'cityVisualization',\n 'agentEvents',\n 'codeViewer',\n 'markdownViewer',\n ],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'quality-check': {\n id: 'quality-check',\n name: 'Quality Check',\n description:\n 'Package information, tools, and dependencies on left; city visualization map in middle; multi terminal and code viewer on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['packageInfo', 'tools', 'dependencies'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'cityVisualization',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'codeViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 45, right: 35 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n drawing: {\n id: 'drawing',\n name: 'Drawing',\n description:\n 'Drawings and docs, excalidraw diagram, multi terminal and markdown viewer',\n layout: {\n left: {\n type: 'tabs',\n panels: ['drawings', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: 'excalidrawDiagram',\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'old-school': {\n id: 'old-school',\n name: 'Old School',\n description:\n 'File tree, search, git changes, and docs on left; code viewer and markdown viewer in middle; multi terminal and city map on right (collapsed)',\n layout: {\n left: {\n type: 'tabs',\n panels: ['fileTree', 'search', 'gitChanges', 'docs'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n middle: {\n type: 'tabs',\n panels: ['codeViewer', 'markdownViewer'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n right: {\n type: 'tabs',\n panels: ['multiTerminal', 'cityVisualization'],\n config: { defaultActiveTab: 0, tabPosition: 'top' },\n },\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: true },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n 'principal-office': {\n id: 'principal-office',\n name: 'Principal Office',\n description:\n 'Alexandria docs on left, MDX editor in middle, multi terminal on right',\n layout: {\n left: 'docs',\n middle: 'mdxEditor',\n right: 'multiTerminal',\n },\n defaultSizes: { left: 20, middle: 50, right: 30 },\n defaultCollapsed: { left: false, right: false },\n createdAt: now,\n updatedAt: now,\n isBuiltIn: true,\n },\n };\n }\n\n /**\n * Initialize workspace layouts with built-in defaults if none exist\n */\n static async initializeWorkspaceLayouts(): Promise<void> {\n const userWorkspaces = await this.adapter.loadWorkspacePresets();\n\n // Only initialize if there are no user workspaces yet\n // Built-in workspaces are always available via getBuiltInWorkspaceLayouts()\n if (Object.keys(userWorkspaces).length === 0) {\n // Nothing to do - built-in workspaces are always available\n // This method exists for compatibility with electron-app\n }\n }\n}\n","/**\n * useWorkspace - React hook for workspace management\n * Provides easy access to workspace operations and state\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport { WorkspaceLayoutService } from '../services/WorkspaceLayoutService';\nimport type {\n WorkspaceLayout,\n RepositoryWorkspaceState,\n CreateWorkspaceOptions,\n UpdateWorkspaceOptions,\n} from '../types/workspace.types';\n\nexport interface UseWorkspaceOptions {\n /**\n * Optional repository key for repository-specific state\n * If provided, the hook will manage repository state\n */\n repositoryKey?: string;\n\n /**\n * Auto-initialize repository state if it doesn't exist\n */\n autoInitialize?: boolean;\n\n /**\n * Default workspace to use for auto-initialization\n */\n defaultWorkspaceId?: string;\n}\n\nexport interface UseWorkspaceReturn {\n /**\n * All available workspaces (built-in + user-created)\n */\n workspaces: Record<string, WorkspaceLayout>;\n\n /**\n * Current repository state (if repositoryKey provided)\n */\n repositoryState: RepositoryWorkspaceState | null;\n\n /**\n * Current active workspace (if repositoryKey provided)\n */\n activeWorkspace: WorkspaceLayout | null;\n\n /**\n * Loading state\n */\n loading: boolean;\n\n /**\n * Error state\n */\n error: Error | null;\n\n /**\n * Get a specific workspace by ID\n */\n getWorkspace: (id: string) => Promise<WorkspaceLayout | null>;\n\n /**\n * Create a new workspace\n */\n createWorkspace: (options: CreateWorkspaceOptions) => Promise<WorkspaceLayout>;\n\n /**\n * Update an existing workspace\n */\n updateWorkspace: (\n id: string,\n updates: UpdateWorkspaceOptions,\n ) => Promise<WorkspaceLayout | null>;\n\n /**\n * Delete a workspace\n */\n deleteWorkspace: (id: string) => Promise<boolean>;\n\n /**\n * Apply a workspace to the current repository\n */\n applyWorkspace: (workspaceId: string) => Promise<void>;\n\n /**\n * Update repository sizes\n */\n updateSizes: (sizes: {\n left: number;\n middle: number;\n right: number;\n }) => Promise<void>;\n\n /**\n * Update repository collapsed state\n */\n updateCollapsed: (collapsed: {\n left?: boolean;\n right?: boolean;\n }) => Promise<void>;\n\n /**\n * Reset repository to workspace defaults\n */\n resetToDefaults: () => Promise<void>;\n\n /**\n * Refresh workspace data\n */\n refresh: () => Promise<void>;\n}\n\nexport function useWorkspace(\n options: UseWorkspaceOptions = {},\n): UseWorkspaceReturn {\n const { repositoryKey, autoInitialize = false, defaultWorkspaceId } = options;\n\n const [workspaces, setWorkspaces] = useState<Record<string, WorkspaceLayout>>(\n {},\n );\n const [repositoryState, setRepositoryState] =\n useState<RepositoryWorkspaceState | null>(null);\n const [activeWorkspace, setActiveWorkspace] =\n useState<WorkspaceLayout | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n /**\n * Load all workspaces and repository state\n */\n const loadData = useCallback(async () => {\n try {\n setLoading(true);\n setError(null);\n\n // Load all workspaces\n const allWorkspaces =\n await WorkspaceLayoutService.getWorkspaceLayouts();\n setWorkspaces(allWorkspaces);\n\n // Load repository state if repositoryKey provided\n if (repositoryKey) {\n let repoState =\n await WorkspaceLayoutService.getRepositoryState(repositoryKey);\n\n // Auto-initialize if requested and state doesn't exist\n if (!repoState && autoInitialize) {\n const workspaceId =\n defaultWorkspaceId || 'project-management'; // Default to project-management\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n\n if (workspace) {\n repoState = {\n workspaceId,\n sizes: workspace.defaultSizes || {\n left: 20,\n middle: 45,\n right: 35,\n },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(\n repositoryKey,\n repoState,\n );\n }\n }\n\n setRepositoryState(repoState);\n\n // Load active workspace if repository has one\n if (repoState?.workspaceId) {\n const workspace = await WorkspaceLayoutService.getWorkspaceLayout(\n repoState.workspaceId,\n );\n setActiveWorkspace(workspace);\n } else {\n setActiveWorkspace(null);\n }\n }\n } catch (err) {\n setError(err as Error);\n console.error('Failed to load workspace data:', err);\n } finally {\n setLoading(false);\n }\n }, [repositoryKey, autoInitialize, defaultWorkspaceId]);\n\n /**\n * Initial load\n */\n useEffect(() => {\n loadData();\n }, [loadData]);\n\n /**\n * Get a specific workspace\n */\n const getWorkspace = useCallback(\n async (id: string) => {\n return await WorkspaceLayoutService.getWorkspaceLayout(id);\n },\n [],\n );\n\n /**\n * Create a new workspace\n */\n const createWorkspace = useCallback(\n async (createOptions: CreateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.createWorkspaceLayout(createOptions);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Update an existing workspace\n */\n const updateWorkspace = useCallback(\n async (id: string, updates: UpdateWorkspaceOptions) => {\n const workspace =\n await WorkspaceLayoutService.updateWorkspaceLayout(id, updates);\n await loadData(); // Refresh\n return workspace;\n },\n [loadData],\n );\n\n /**\n * Delete a workspace\n */\n const deleteWorkspace = useCallback(\n async (id: string) => {\n const success = await WorkspaceLayoutService.deleteWorkspaceLayout(id);\n if (success) {\n await loadData(); // Refresh\n }\n return success;\n },\n [loadData],\n );\n\n /**\n * Apply a workspace to the current repository\n */\n const applyWorkspace = useCallback(\n async (workspaceId: string) => {\n if (!repositoryKey) {\n console.error('Cannot apply workspace without repositoryKey');\n return;\n }\n\n const workspace =\n await WorkspaceLayoutService.getWorkspaceLayout(workspaceId);\n if (!workspace) {\n console.error(`Workspace ${workspaceId} not found`);\n return;\n }\n\n const newState: RepositoryWorkspaceState = {\n workspaceId,\n sizes: workspace.defaultSizes || { left: 20, middle: 45, right: 35 },\n collapsed: workspace.defaultCollapsed || {\n left: false,\n right: false,\n },\n };\n\n await WorkspaceLayoutService.setRepositoryState(repositoryKey, newState);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository sizes\n */\n const updateSizes = useCallback(\n async (sizes: { left: number; middle: number; right: number }) => {\n if (!repositoryKey) {\n console.error('Cannot update sizes without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositorySizes(repositoryKey, sizes);\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Update repository collapsed state\n */\n const updateCollapsed = useCallback(\n async (collapsed: { left?: boolean; right?: boolean }) => {\n if (!repositoryKey) {\n console.error('Cannot update collapsed without repositoryKey');\n return;\n }\n\n await WorkspaceLayoutService.updateRepositoryCollapsed(\n repositoryKey,\n collapsed,\n );\n await loadData(); // Refresh\n },\n [repositoryKey, loadData],\n );\n\n /**\n * Reset repository to workspace defaults\n */\n const resetToDefaults = useCallback(async () => {\n if (!repositoryKey || !repositoryState?.workspaceId) {\n console.error('Cannot reset without repositoryKey and active workspace');\n return;\n }\n\n await WorkspaceLayoutService.resetRepositoryToWorkspaceDefaults(\n repositoryKey,\n repositoryState.workspaceId,\n );\n await loadData(); // Refresh\n }, [repositoryKey, repositoryState?.workspaceId, loadData]);\n\n /**\n * Refresh workspace data\n */\n const refresh = useCallback(async () => {\n await loadData();\n }, [loadData]);\n\n return {\n workspaces,\n repositoryState,\n activeWorkspace,\n loading,\n error,\n getWorkspace,\n createWorkspace,\n updateWorkspace,\n deleteWorkspace,\n applyWorkspace,\n updateSizes,\n updateCollapsed,\n resetToDefaults,\n refresh,\n };\n}\n"],"names":["LocalStoragePersistenceAdapter","__publicField","viewKey","stored","error","state","usePanelPersistence","options","defaultSizes","panelType","adapter","sizes","setSizes","useState","collapsed","setCollapsed","prevCollapsedRef","useRef","lastNonZeroSizesRef","saveTimeoutRef","pendingPersistSizesRef","useEffect","saved","updateLastNonZeroSizes","useCallback","incomingSizes","getFallbackSize","panel","storedSize","prevDefaultSizesRef","leftChanged","rightChanged","savePreferences","newSizes","handlePanelResize","sanitizedSizes","shouldPersist","leftCollapsed","leftSize","fallback","rightCollapsed","rightSize","pendingSizes","handleLeftCollapseComplete","handleLeftExpandComplete","handleRightCollapseComplete","handleRightExpandComplete","LocalStorageWorkspaceAdapter","presets","repositoryKey","key","allStates","i","parseError","WorkspaceLayoutService","builtInWorkspaces","id","userWorkspaces","workspace","updatedPresets","updates","existingWorkspace","updatedWorkspace","currentState","repoState","hasSizeDeviation","hasCollapsedDeviation","workspaceId","layout","workspaces","layout1","layout2","name","now","useWorkspace","autoInitialize","defaultWorkspaceId","setWorkspaces","repositoryState","setRepositoryState","activeWorkspace","setActiveWorkspace","loading","setLoading","setError","loadData","allWorkspaces","err","getWorkspace","createWorkspace","createOptions","updateWorkspace","deleteWorkspace","success","applyWorkspace","newState","updateSizes","updateCollapsed","resetToDefaults","refresh"],"mappings":"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"}
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.animated-resizable-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.hybrid-panel{overflow:auto;height:100%;background-color:var(--panel-background)}.resize-handle{width:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:col-resize;position:relative;transition:background-color .2s,opacity .3s,width .3s}.resize-handle.collapsed{opacity:0;pointer-events:none}.resize-handle:hover{background-color:var(--panel-handle-hover)}.resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{height:100%;width:100%;display:flex;align-items:center;justify-content:center;position:relative}.collapse-toggle{position:absolute;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.animated-vertical-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.vertical-panel{overflow:auto;width:100%;background-color:var(--panel-background)}.panel-content-wrapper{width:100%;height:100%;overflow:auto}.vertical-resize-handle{height:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:row-resize;position:relative;transition:background-color .2s,opacity .3s,height .3s}.vertical-resize-handle.collapsed{opacity:0;pointer-events:none}.vertical-resize-handle:hover{background-color:var(--panel-handle-hover)}.vertical-resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{width:100%;height:100%;display:flex;align-items:center;justify-content:center;gap:8px;position:relative}.collapse-toggle{background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.collapse-toggle:hover:not(:disabled){background-color:var(--panel-button-hover);box-shadow:0 2px 6px #00000026}.collapse-toggle:active:not(:disabled){opacity:.8}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important}.panel-content-wrapper{flex:1;overflow:auto;will-change:opacity;box-sizing:border-box}.resize-handle.collapsed{width:0!important}.tab-group{display:flex;height:100%;width:100%;overflow:hidden}.tab-group.tab-position-top,.tab-group.tab-position-bottom{flex-direction:column}.tab-group.tab-position-left,.tab-group.tab-position-right{flex-direction:row}.tab-list{display:flex;background:var(--tab-list-bg, #f5f5f5);border-bottom:1px solid var(--tab-border, #ddd);gap:0;padding:0;flex-shrink:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.tab-list::-webkit-scrollbar{display:none}.tab-position-top .tab-list,.tab-position-bottom .tab-list{width:100%}.tab-list.centered{justify-content:flex-start}@media (min-width: 0){.tab-list.centered{justify-content:center}.tab-list.centered:has(.tab-button:nth-child(n)){justify-content:flex-start}}.tab-position-bottom .tab-list{border-bottom:none;border-top:none}.tab-position-left .tab-list,.tab-position-right .tab-list{flex-direction:column;border-bottom:none;border-right:none;width:auto;min-width:120px}.tab-position-top .tab-button,.tab-position-bottom .tab-button{flex:1 1 0;min-width:40px;max-width:100%}.tab-button{background:var(--tab-bg, #fff);border:1px solid var(--tab-border, #ddd);border-radius:0;padding:8px 16px;cursor:pointer;font-size:14px;font-weight:500;color:var(--tab-text, #333);transition:all .2s ease;white-space:nowrap;display:flex;align-items:center;justify-content:center;gap:6px;height:40px;line-height:1;box-sizing:border-box;container-type:inline-size}.tab-position-top .tab-button{border-bottom:none;border-top:none;border-left:none}.tab-position-top .tab-button:last-child{border-right:none}.tab-position-bottom .tab-button{border-top:none;border-bottom:none;border-left:none}.tab-position-bottom .tab-button:last-child{border-right:none}.tab-position-left .tab-button{border-right:none;border-bottom:none}.tab-position-left .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-position-right .tab-button{border-left:none;border-bottom:none}.tab-position-right .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-icon{display:inline-flex;align-items:center;justify-content:center}.tab-label{display:none}@container (min-width: 250px){.tab-label{display:inline}.tab-icon{display:none}}.tab-button:hover{background:var(--tab-bg-hover, #f9f9f9)}.tab-button.active{background:var(--tab-bg-active, #007bff);color:var(--tab-text-active, #fff);border-color:var(--tab-border-active, #007bff)}.tab-button:focus-visible{outline:2px solid var(--tab-focus, #007bff);outline-offset:2px}.tab-content{flex:1;overflow:auto;background:var(--tab-content-bg, #fff)}.tab-group-empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--tab-empty-text, #999);font-style:italic}.three-panel-layout{height:100%;width:100%;display:flex;flex-direction:column;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item{display:flex;flex-direction:column;overflow:hidden;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item.collapsible-panel{will-change:flex}.three-panel-item.collapsible-panel.animating{pointer-events:none}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important;max-width:0!important;width:0!important;overflow:hidden!important;visibility:hidden}.three-panel-item.middle-panel{flex:1;min-width:200px}.panel-content-wrapper{flex:1;overflow-x:hidden;overflow-y:auto;will-change:opacity;box-sizing:border-box}.resize-handle{position:relative;display:flex;align-items:center;justify-content:center;width:1px!important;cursor:col-resize;background:var(--panel-border);overflow:visible!important}.resize-handle:before{content:"";position:absolute;inset:0 -10px;background:transparent}.resize-handle:after{content:"";position:absolute;inset:0 -10px;background:var(--panel-handle);opacity:0;transition:opacity .2s ease;z-index:-1}.resize-handle:hover:after{opacity:1}.resize-handle:hover{background:var(--panel-handle-hover)}.resize-handle:active:after{opacity:1;background:var(--panel-handle-active)}.resize-handle:active{background:var(--panel-handle-active)}.resize-handle.collapsed{width:0!important;visibility:hidden}.resize-handle.left-handle.collapsed{margin-right:-1px}.resize-handle.right-handle.collapsed{margin-left:-1px}.handle-bar{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;z-index:2}.collapse-toggle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:20px;height:40px;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);border-radius:4px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;color:var(--panel-button-icon);transition:all .2s ease;z-index:10;padding:0;line-height:1}.collapse-toggle:hover{background:var(--panel-button-hover)}.collapse-toggle:active{opacity:.8}.collapse-toggle:disabled{opacity:.5;cursor:not-allowed}@media (max-width: 768px){.resize-handle:before{left:-8px;right:-8px}.resize-handle:after{left:-8px;right:-8px}.collapse-toggle{width:24px;height:48px;font-size:14px}}@keyframes wiggle{0%{transform:rotate(0)}25%{transform:rotate(1deg)}50%{transform:rotate(0)}75%{transform:rotate(-1deg)}to{transform:rotate(0)}}@keyframes scaleIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}@keyframes pulse{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 0 8px #3b82f600}}.editable-panel-layout{position:relative;width:100%;height:100%}.editable-panel-layout.edit-mode-active{background:#0000000d}[data-slot][data-edit-mode=true]{transform:scale(.95);transform-origin:center center;cursor:grab;will-change:transform;transition:transform .3s cubic-bezier(.4,0,.2,1)}[data-slot][data-edit-mode=true]:active{cursor:grabbing}[data-slot][data-dragging=true]{cursor:grabbing!important;transform:scale(.95)!important;transition:none!important}.edit-mode-active [data-slot][data-edit-mode=true]{transform-origin:center center}.slot-with-overlay{position:relative;width:100%;height:100%}.slot-with-overlay.dragging{opacity:0;pointer-events:none}.slot-edit-overlay{position:absolute;inset:0;background:transparent;border:none;pointer-events:auto;cursor:grab;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center}.slot-edit-overlay:hover{background:#3b82f608}.slot-edit-overlay:active{cursor:grabbing}.drag-indicator,.slot-position-label{display:none}.edit-mode-toggle{position:absolute;top:16px;right:16px;z-index:1000;padding:8px 16px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;box-shadow:0 2px 4px #0000001a}.edit-mode-toggle:hover{background:#f9fafb;box-shadow:0 4px 6px #0000001a}.edit-mode-toggle.active{background:#3b82f6;color:#fff;border-color:#2563eb}.edit-mode-overlay{position:fixed;inset:0;background:#0000004d;z-index:998;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.edit-mode-configurator{position:relative;z-index:999;padding:20px;animation:scaleIn .3s ease}.panel-slot{position:relative;min-height:120px;border:2px dashed transparent;border-radius:12px;transition:all .3s ease;padding:12px}.panel-slot.edit-mode{border-color:#d1d5db;background:#ffffff80}.panel-slot.drag-over{border-color:#3b82f6;background:#3b82f61a;box-shadow:0 0 0 4px #3b82f61a}.panel-slot.empty{display:flex;align-items:center;justify-content:center;color:#9ca3af;font-size:14px}.draggable-panel{position:relative;padding:12px 16px;margin:8px 0;border-radius:8px;background:#fff;border:1px solid #e5e7eb;cursor:grab;transition:all .2s ease;-webkit-user-select:none;user-select:none}.draggable-panel:hover{border-color:#3b82f6;box-shadow:0 2px 8px #0000001a}.draggable-panel.dragging{opacity:.5;cursor:grabbing}.draggable-panel.edit-mode{animation:wiggle .4s ease-in-out infinite;transform-origin:center}.draggable-panel.edit-mode:nth-child(odd){animation-delay:.1s}.draggable-panel.edit-mode:nth-child(2n){animation-delay:.2s}.remove-button{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#ef4444;color:#fff;border:2px solid white;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;font-weight:700;opacity:0;transform:scale(0);transition:all .2s ease;z-index:10;box-shadow:0 2px 4px #0003}.draggable-panel.edit-mode .remove-button{opacity:1;transform:scale(1);animation:pulse 2s infinite}.remove-button:hover{background:#dc2626;transform:scale(1.1)}.panel-label{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500;color:#374151}.panel-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.drag-handle{display:none;width:24px;height:24px;opacity:.4;cursor:grab}.edit-mode .drag-handle{display:flex;align-items:center;justify-content:center}.drag-overlay{padding:12px 16px;border-radius:8px;background:#fff;border:2px solid #3b82f6;box-shadow:0 8px 16px #0003;cursor:grabbing;opacity:.9}.panel-group{border:2px solid #e5e7eb;border-radius:12px;padding:8px;background:#fff}.panel-group.edit-mode{animation:wiggle .5s ease-in-out infinite;border-color:#3b82f6}.panel-group-header{display:flex;justify-content:space-between;align-items:center;padding:8px;border-bottom:1px solid #e5e7eb;margin-bottom:8px}.panel-group-title{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase}.available-panels{background:#fff;border-radius:12px;padding:16px;box-shadow:0 4px 6px #0000001a}.available-panels-title{font-size:16px;font-weight:600;color:#374151;margin-bottom:12px}.available-panels-list{display:flex;flex-direction:column;gap:8px}.slot-label{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center;gap:8px}.slot-indicator{width:8px;height:8px;border-radius:50%;background:#d1d5db}.slot-indicator.active{background:#3b82f6}.action-buttons{position:fixed;bottom:24px;left:50%;transform:translate(-50%);z-index:1001;display:flex;gap:12px;padding:12px 24px;background:#fff;border-radius:12px;box-shadow:0 8px 16px #00000026;animation:scaleIn .3s ease}.action-button{padding:10px 20px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease}.action-button:hover{background:#f9fafb}.action-button.primary{background:#3b82f6;color:#fff;border-color:#2563eb}.action-button.primary:hover{background:#2563eb}@media (max-width: 768px){.edit-mode-toggle{top:8px;right:8px;padding:6px 12px;font-size:12px}.action-buttons{bottom:16px;padding:10px 16px}.action-button{padding:8px 16px;font-size:13px}}@media (prefers-color-scheme: dark){.edit-mode-toggle{background:#1f2937;color:#f9fafb;border-color:#374151}.edit-mode-toggle:hover{background:#374151}.draggable-panel{background:#1f2937;color:#f9fafb;border-color:#374151}.available-panels{background:#1f2937}.panel-label{color:#f9fafb}.action-buttons{background:#1f2937}.action-button{background:#1f2937;color:#f9fafb;border-color:#374151}.action-button:hover{background:#374151}}.panel-configurator{display:flex;flex-direction:column;gap:2rem;padding:1.5rem;background:var(--configurator-bg);border-radius:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.configurator-section{display:flex;flex-direction:column;gap:1rem}.section-title{margin:0;font-size:.875rem;font-weight:600;color:var(--configurator-title);text-transform:uppercase;letter-spacing:.05em;text-align:center}.slots-container{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;width:75%;margin:0 auto;align-items:start}.slot{position:relative;aspect-ratio:1 / 1.3;width:100%;padding:2.5rem 1rem 1rem;background:var(--slot-bg);border:2px solid var(--slot-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem;overflow:hidden;box-sizing:border-box}.slot:hover{border-color:var(--slot-border-hover);box-shadow:0 2px 4px #0000000d}.slot.selected{border-color:var(--slot-border-selected);background:var(--slot-bg-selected);box-shadow:0 0 0 3px var(--slot-bg-selected)}.slot.empty{border-style:dashed}.slot-label{font-size:.75rem;font-weight:600;color:var(--slot-label);text-transform:capitalize;text-align:left}.slot[data-position=middle] .slot-label,.slot[data-position=middle] .slot-panel-name,.slot[data-position=middle] .slot-empty-state{text-align:center}.slot[data-position=right] .slot-label,.slot[data-position=right] .slot-panel-name,.slot[data-position=right] .slot-empty-state{text-align:right}.slot-content{flex:1;display:flex;flex-direction:column;gap:.5rem;overflow-y:auto;min-height:0}.slot-panel-name{font-weight:600;color:var(--slot-content-text);font-size:.875rem}.slot-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--slot-preview-bg);border-radius:4px;border:1px solid var(--slot-preview-border);font-size:.75rem;color:var(--slot-preview-text)}.slot-empty-state{flex:1;display:flex;align-items:center;justify-content:center;color:var(--slot-empty-text);font-size:.875rem;font-style:italic;min-height:0}.slot-clear-btn{position:absolute;top:.25rem;right:.25rem;width:24px;height:24px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:4px;cursor:pointer;font-size:1.25rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease}.slot:hover .slot-clear-btn{opacity:1}.slot-clear-btn:hover{background:var(--clear-btn-hover);transform:scale(1.1)}.available-panels{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.75rem;width:75%;margin:0 auto}.available-panel{min-height:80px;padding:.75rem;background:var(--panel-bg);border:2px solid var(--panel-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem}.available-panel:hover{border-color:var(--panel-border-hover);box-shadow:0 2px 4px #0000000d;transform:translateY(-2px)}.available-panel.selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 3px var(--panel-bg-selected)}.available-panel.in-use{opacity:.5;border-style:dashed}.available-panel.in-use:hover{transform:translateY(0);opacity:.6}.panel-label{font-weight:600;color:var(--panel-label-text);font-size:.875rem}.panel-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--panel-preview-bg);border-radius:4px;font-size:.75rem;color:var(--panel-preview-text)}.selection-hint{padding:.75rem 1rem;background:var(--hint-bg);border:1px solid var(--hint-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}.available-panel.multi-selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 2px var(--panel-bg-selected)}.multi-select-badge{margin-left:.5rem;padding:.25rem .5rem;background:var(--panel-border-selected);color:#fff;font-size:.75rem;border-radius:12px;font-weight:400}.multi-select-hint{background:var(--panel-bg-selected);border-color:var(--panel-border-selected)}.slot.tab-group{border-style:solid}.group-content{position:relative;overflow-y:auto;min-height:0}.group-badge{font-size:.75rem;font-weight:600;color:var(--panel-border-selected);margin-bottom:.5rem}.group-panels{display:flex;flex-direction:column;gap:.25rem;flex:1;overflow-y:auto;min-height:0}.group-panel-label{font-size:.75rem;color:var(--slot-content-text);padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.slot-panel-label{font-weight:600;color:var(--slot-content-text);font-size:.875rem;text-align:center}.create-tab-group-btn{margin-top:.5rem;padding:.5rem .75rem;background:var(--panel-border-selected);color:#fff;border:none;border-radius:4px;font-size:.75rem;font-weight:600;cursor:pointer;transition:all .2s ease}.create-tab-group-btn:hover{transform:scale(1.05);box-shadow:0 2px 4px #0000001a}.tab-mode-toggle{position:absolute;top:.5rem;left:.5rem;padding:.25rem .5rem;background:var(--slot-bg);border:1px solid var(--slot-border);border-radius:4px;font-size:.85rem;cursor:pointer;transition:all .2s ease;z-index:10;opacity:.7;color:var(--slot-content-text)}.tab-mode-toggle svg{display:block}.tab-mode-toggle:hover{opacity:1;border-color:var(--slot-border-hover);background:var(--slot-preview-bg)}.tab-mode-toggle.active{opacity:1;background:var(--panel-border-selected);color:#fff;border-color:var(--panel-border-selected)}.tab-config-controls{margin-bottom:.5rem}.tab-config-label{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:var(--slot-label)}.tab-config-label select{padding:.25rem .5rem;border:1px solid var(--slot-border);border-radius:3px;background:var(--slot-bg);color:var(--slot-content-text);font-size:.7rem;cursor:pointer}.group-panel-item{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.group-panel-label{flex:1;font-size:.75rem;color:var(--slot-content-text);display:flex;align-items:center;gap:.25rem}.default-badge{color:var(--panel-border-selected);font-size:.9em}.remove-from-group-btn{width:18px;height:18px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:3px;cursor:pointer;font-size:1rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s ease}.remove-from-group-btn:hover{opacity:1;transform:scale(1.1)}.usage-hint{padding:.75rem 1rem;background:var(--slot-preview-bg);border:1px solid var(--slot-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}.snap-carousel-container{display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;gap:var(--snap-carousel-gap, 0px);padding:0;margin:0;width:100%;height:100%;background-color:var(--panel-background);box-sizing:border-box;position:relative;left:0;transform:none;container-type:inline-size;-ms-overflow-style:none;scrollbar-width:none}.snap-carousel-container::-webkit-scrollbar{display:none}.snap-carousel-panel{flex:0 0 auto;scroll-snap-align:start;scroll-snap-stop:always;width:var(--snap-carousel-panel-width, 33.33%);height:100%;box-sizing:border-box;overflow:hidden}@media (max-width: 540px){.snap-carousel-panel{min-width:280px}}
|
|
1
|
+
.animated-resizable-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.hybrid-panel{overflow:auto;height:100%;background-color:var(--panel-background)}.resize-handle{width:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:col-resize;position:relative;transition:background-color .2s,opacity .3s,width .3s}.resize-handle.collapsed{opacity:0;pointer-events:none}.resize-handle:hover{background-color:var(--panel-handle-hover)}.resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{height:100%;width:100%;display:flex;align-items:center;justify-content:center;position:relative}.collapse-toggle{position:absolute;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.animated-vertical-layout{width:100%;height:100%;position:relative;background-color:var(--panel-background)}.vertical-panel{overflow:auto;width:100%;background-color:var(--panel-background)}.panel-content-wrapper{width:100%;height:100%;overflow:auto}.vertical-resize-handle{height:8px;background-color:var(--panel-handle);display:flex;justify-content:center;align-items:center;cursor:row-resize;position:relative;transition:background-color .2s,opacity .3s,height .3s}.vertical-resize-handle.collapsed{opacity:0;pointer-events:none}.vertical-resize-handle:hover{background-color:var(--panel-handle-hover)}.vertical-resize-handle:active{background-color:var(--panel-handle-active)}.handle-bar{width:100%;height:100%;display:flex;align-items:center;justify-content:center;gap:8px;position:relative}.collapse-toggle{background:var(--panel-button-bg);border:1px solid var(--panel-button-border);padding:4px 8px;cursor:pointer;color:var(--panel-button-icon);font-size:14px;outline:none;display:flex;align-items:center;justify-content:center;border-radius:4px;z-index:10;transition:all .2s;box-shadow:0 2px 4px #0000001a}.collapse-toggle:hover:not(:disabled){background-color:var(--panel-button-hover);box-shadow:0 2px 6px #00000026}.collapse-toggle:active:not(:disabled){opacity:.8}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important}.panel-content-wrapper{flex:1;overflow:auto;will-change:opacity;box-sizing:border-box}.resize-handle.collapsed{width:0!important}.tab-group{display:flex;height:100%;width:100%;overflow:hidden}.tab-group.tab-position-top,.tab-group.tab-position-bottom{flex-direction:column}.tab-group.tab-position-left,.tab-group.tab-position-right{flex-direction:row}.tab-list{display:flex;background:var(--tab-list-bg, #f5f5f5);border-bottom:1px solid var(--tab-border, #ddd);gap:0;padding:0;flex-shrink:0;overflow-x:auto;overflow-y:hidden;scrollbar-width:none;-ms-overflow-style:none}.tab-list::-webkit-scrollbar{display:none}.tab-position-top .tab-list,.tab-position-bottom .tab-list{width:100%}.tab-list.centered{justify-content:flex-start}@media(min-width:0){.tab-list.centered{justify-content:center}.tab-list.centered:has(.tab-button:nth-child(n)){justify-content:flex-start}}.tab-position-bottom .tab-list{border-bottom:none;border-top:none}.tab-position-left .tab-list,.tab-position-right .tab-list{flex-direction:column;border-bottom:none;border-right:none;width:auto;min-width:120px}.tab-position-top .tab-button,.tab-position-bottom .tab-button{flex:1 1 0;min-width:40px;max-width:100%}.tab-button{background:var(--tab-bg, #fff);border:1px solid var(--tab-border, #ddd);border-radius:0;padding:8px 16px;cursor:pointer;font-size:14px;font-weight:500;color:var(--tab-text, #333);transition:all .2s ease;white-space:nowrap;display:flex;align-items:center;justify-content:center;gap:6px;height:40px;line-height:1;box-sizing:border-box;container-type:inline-size}.tab-position-top .tab-button{border-bottom:none;border-top:none;border-left:none}.tab-position-top .tab-button:last-child{border-right:none}.tab-position-bottom .tab-button{border-top:none;border-bottom:none;border-left:none}.tab-position-bottom .tab-button:last-child{border-right:none}.tab-position-left .tab-button{border-right:none;border-bottom:none}.tab-position-left .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-position-right .tab-button{border-left:none;border-bottom:none}.tab-position-right .tab-button:last-child{border-bottom:1px solid var(--tab-border, #ddd)}.tab-icon{display:inline-flex;align-items:center;justify-content:center}.tab-label{display:none}@container (min-width: 250px){.tab-label{display:inline}.tab-icon{display:none}}.tab-button:hover{background:var(--tab-bg-hover, #f9f9f9)}.tab-button.active{background:var(--tab-bg-active, #007bff);color:var(--tab-text-active, #fff);border-color:var(--tab-border-active, #007bff)}.tab-button:focus-visible{outline:2px solid var(--tab-focus, #007bff);outline-offset:2px}.tab-content{flex:1;overflow:auto;background:var(--tab-content-bg, #fff)}.tab-group-empty{display:flex;align-items:center;justify-content:center;height:100%;color:var(--tab-empty-text, #999);font-style:italic}.three-panel-layout{height:100%;width:100%;display:flex;flex-direction:column;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item{display:flex;flex-direction:column;overflow:hidden;position:relative;background-color:var(--panel-background);box-sizing:border-box}.three-panel-item[data-edit-mode=true]{background-color:var(--panel-accent-bg);border-radius:12px}.three-panel-item.collapsible-panel{will-change:flex}.three-panel-item.collapsible-panel.animating{pointer-events:none}.three-panel-item.collapsible-panel.collapsed{flex:0!important;min-width:0!important;max-width:0!important;width:0!important;overflow:hidden!important;visibility:hidden}.three-panel-item.middle-panel{flex:1;min-width:200px}.panel-content-wrapper{flex:1;overflow-x:hidden;overflow-y:auto;will-change:opacity;box-sizing:border-box}.resize-handle{position:relative;display:flex;align-items:center;justify-content:center;width:1px!important;cursor:col-resize;background:var(--panel-border);overflow:visible!important}.resize-handle:before{content:"";position:absolute;inset:0 -10px;background:transparent}.resize-handle:after{content:"";position:absolute;inset:0 -10px;background:var(--panel-handle);opacity:0;transition:opacity .2s ease;z-index:-1}.resize-handle:hover:after{opacity:1}.resize-handle:hover{background:var(--panel-handle-hover)}.resize-handle:active:after{opacity:1;background:var(--panel-handle-active)}.resize-handle:active{background:var(--panel-handle-active)}.resize-handle.collapsed{width:0!important;visibility:hidden}.resize-handle.left-handle.collapsed{margin-right:-1px}.resize-handle.right-handle.collapsed{margin-left:-1px}.handle-bar{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;z-index:2}.collapse-toggle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:20px;height:40px;background:var(--panel-button-bg);border:1px solid var(--panel-button-border);border-radius:4px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;color:var(--panel-button-icon);transition:all .2s ease;z-index:10;padding:0;line-height:1}.collapse-toggle:hover{background:var(--panel-button-hover)}.collapse-toggle:active{opacity:.8}.collapse-toggle:disabled{opacity:.5;cursor:not-allowed}@media(max-width:768px){.resize-handle:before{left:-8px;right:-8px}.resize-handle:after{left:-8px;right:-8px}.collapse-toggle{width:24px;height:48px;font-size:14px}}.snap-carousel-container{display:flex;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory;scroll-behavior:smooth;gap:var(--snap-carousel-gap, 0px);padding:0;margin:0;width:100%;height:100%;background-color:var(--panel-background);box-sizing:border-box;position:relative;left:0;transform:none;container-type:inline-size;-ms-overflow-style:none;scrollbar-width:none}.snap-carousel-container::-webkit-scrollbar{display:none}.snap-carousel-panel{flex:0 0 auto;scroll-snap-align:start;scroll-snap-stop:always;width:var(--snap-carousel-panel-width, 33.33%);height:100%;box-sizing:border-box;overflow:hidden}@media(max-width:540px){.snap-carousel-panel{min-width:280px}}@keyframes wiggle{0%{transform:rotate(0)}25%{transform:rotate(1deg)}50%{transform:rotate(0)}75%{transform:rotate(-1deg)}to{transform:rotate(0)}}@keyframes scaleIn{0%{transform:scale(.95);opacity:0}to{transform:scale(1);opacity:1}}@keyframes pulse{0%,to{box-shadow:0 0 #3b82f666}50%{box-shadow:0 0 0 8px #3b82f600}}.editable-panel-layout{position:relative;width:100%;height:100%}.editable-panel-layout.edit-mode-active{background:#0000000d}[data-slot][data-edit-mode=true]{transform:scale(.95);transform-origin:center center;cursor:grab;will-change:transform;transition:transform .3s cubic-bezier(.4,0,.2,1)}[data-slot][data-edit-mode=true]:active{cursor:grabbing}[data-slot][data-dragging=true]{cursor:grabbing!important;transform:scale(.95)!important;transition:none!important}.edit-mode-active [data-slot][data-edit-mode=true]{transform-origin:center center}.slot-with-overlay{position:relative;width:100%;height:100%}.slot-with-overlay.dragging{opacity:0;pointer-events:none}.slot-edit-overlay{position:absolute;inset:0;background:transparent;border:none;pointer-events:auto;cursor:grab;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center}.slot-edit-overlay:hover{background:#3b82f608}.slot-edit-overlay:active{cursor:grabbing}.drag-indicator,.slot-position-label{display:none}.edit-mode-toggle{position:absolute;top:16px;right:16px;z-index:1000;padding:8px 16px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease;box-shadow:0 2px 4px #0000001a}.edit-mode-toggle:hover{background:#f9fafb;box-shadow:0 4px 6px #0000001a}.edit-mode-toggle.active{background:#3b82f6;color:#fff;border-color:#2563eb}.edit-mode-overlay{position:fixed;inset:0;background:#0000004d;z-index:998;animation:fadeIn .2s ease}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.edit-mode-configurator{position:relative;z-index:999;padding:20px;animation:scaleIn .3s ease}.panel-slot{position:relative;min-height:120px;border:2px dashed transparent;border-radius:12px;transition:all .3s ease;padding:12px}.panel-slot.edit-mode{border-color:#d1d5db;background:#ffffff80}.panel-slot.drag-over{border-color:#3b82f6;background:#3b82f61a;box-shadow:0 0 0 4px #3b82f61a}.panel-slot.empty{display:flex;align-items:center;justify-content:center;color:#9ca3af;font-size:14px}.draggable-panel{position:relative;padding:12px 16px;margin:8px 0;border-radius:8px;background:#fff;border:1px solid #e5e7eb;cursor:grab;transition:all .2s ease;-webkit-user-select:none;user-select:none}.draggable-panel:hover{border-color:#3b82f6;box-shadow:0 2px 8px #0000001a}.draggable-panel.dragging{opacity:.5;cursor:grabbing}.draggable-panel.edit-mode{animation:wiggle .4s ease-in-out infinite;transform-origin:center}.draggable-panel.edit-mode:nth-child(odd){animation-delay:.1s}.draggable-panel.edit-mode:nth-child(2n){animation-delay:.2s}.remove-button{position:absolute;top:-6px;right:-6px;width:20px;height:20px;border-radius:50%;background:#ef4444;color:#fff;border:2px solid white;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:12px;font-weight:700;opacity:0;transform:scale(0);transition:all .2s ease;z-index:10;box-shadow:0 2px 4px #0003}.draggable-panel.edit-mode .remove-button{opacity:1;transform:scale(1);animation:pulse 2s infinite}.remove-button:hover{background:#dc2626;transform:scale(1.1)}.panel-label{display:flex;align-items:center;gap:8px;font-size:14px;font-weight:500;color:#374151}.panel-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center}.drag-handle{display:none;width:24px;height:24px;opacity:.4;cursor:grab}.edit-mode .drag-handle{display:flex;align-items:center;justify-content:center}.drag-overlay{padding:12px 16px;border-radius:8px;background:#fff;border:2px solid #3b82f6;box-shadow:0 8px 16px #0003;cursor:grabbing;opacity:.9}.panel-group{border:2px solid #e5e7eb;border-radius:12px;padding:8px;background:#fff}.panel-group.edit-mode{animation:wiggle .5s ease-in-out infinite;border-color:#3b82f6}.panel-group-header{display:flex;justify-content:space-between;align-items:center;padding:8px;border-bottom:1px solid #e5e7eb;margin-bottom:8px}.panel-group-title{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase}.available-panels{background:#fff;border-radius:12px;padding:16px;box-shadow:0 4px 6px #0000001a}.available-panels-title{font-size:16px;font-weight:600;color:#374151;margin-bottom:12px}.available-panels-list{display:flex;flex-direction:column;gap:8px}.slot-label{font-size:12px;font-weight:600;color:#6b7280;text-transform:uppercase;margin-bottom:8px;display:flex;align-items:center;gap:8px}.slot-indicator{width:8px;height:8px;border-radius:50%;background:#d1d5db}.slot-indicator.active{background:#3b82f6}.action-buttons{position:fixed;bottom:24px;left:50%;transform:translate(-50%);z-index:1001;display:flex;gap:12px;padding:12px 24px;background:#fff;border-radius:12px;box-shadow:0 8px 16px #00000026;animation:scaleIn .3s ease}.action-button{padding:10px 20px;border-radius:8px;border:1px solid #e5e7eb;background:#fff;color:#374151;font-size:14px;font-weight:500;cursor:pointer;transition:all .2s ease}.action-button:hover{background:#f9fafb}.action-button.primary{background:#3b82f6;color:#fff;border-color:#2563eb}.action-button.primary:hover{background:#2563eb}@media(max-width:768px){.edit-mode-toggle{top:8px;right:8px;padding:6px 12px;font-size:12px}.action-buttons{bottom:16px;padding:10px 16px}.action-button{padding:8px 16px;font-size:13px}}@media(prefers-color-scheme:dark){.edit-mode-toggle{background:#1f2937;color:#f9fafb;border-color:#374151}.edit-mode-toggle:hover{background:#374151}.draggable-panel{background:#1f2937;color:#f9fafb;border-color:#374151}.available-panels{background:#1f2937}.panel-label{color:#f9fafb}.action-buttons{background:#1f2937}.action-button{background:#1f2937;color:#f9fafb;border-color:#374151}.action-button:hover{background:#374151}}.panel-configurator{display:flex;flex-direction:column;gap:2rem;padding:1.5rem;background:var(--configurator-bg);border-radius:8px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.configurator-section{display:flex;flex-direction:column;gap:1rem}.section-title{margin:0;font-size:.875rem;font-weight:600;color:var(--configurator-title);text-transform:uppercase;letter-spacing:.05em;text-align:center}.slots-container{display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;width:75%;margin:0 auto;align-items:start}.slot{position:relative;aspect-ratio:1 / 1.3;width:100%;padding:2.5rem 1rem 1rem;background:var(--slot-bg);border:2px solid var(--slot-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem;overflow:hidden;box-sizing:border-box}.slot:hover{border-color:var(--slot-border-hover);box-shadow:0 2px 4px #0000000d}.slot.selected{border-color:var(--slot-border-selected);background:var(--slot-bg-selected);box-shadow:0 0 0 3px var(--slot-bg-selected)}.slot.empty{border-style:dashed}.slot-label{font-size:.75rem;font-weight:600;color:var(--slot-label);text-transform:capitalize;text-align:left}.slot[data-position=middle] .slot-label,.slot[data-position=middle] .slot-panel-name,.slot[data-position=middle] .slot-empty-state{text-align:center}.slot[data-position=right] .slot-label,.slot[data-position=right] .slot-panel-name,.slot[data-position=right] .slot-empty-state{text-align:right}.slot-content{flex:1;display:flex;flex-direction:column;gap:.5rem;overflow-y:auto;min-height:0}.slot-panel-name{font-weight:600;color:var(--slot-content-text);font-size:.875rem}.slot-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--slot-preview-bg);border-radius:4px;border:1px solid var(--slot-preview-border);font-size:.75rem;color:var(--slot-preview-text)}.slot-empty-state{flex:1;display:flex;align-items:center;justify-content:center;color:var(--slot-empty-text);font-size:.875rem;font-style:italic;min-height:0}.slot-clear-btn{position:absolute;top:.25rem;right:.25rem;width:24px;height:24px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:4px;cursor:pointer;font-size:1.25rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s ease}.slot:hover .slot-clear-btn{opacity:1}.slot-clear-btn:hover{background:var(--clear-btn-hover);transform:scale(1.1)}.available-panels{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.75rem;width:75%;margin:0 auto}.available-panel{min-height:80px;padding:.75rem;background:var(--panel-bg);border:2px solid var(--panel-border);border-radius:6px;cursor:pointer;transition:all .2s ease;display:flex;flex-direction:column;gap:.5rem}.available-panel:hover{border-color:var(--panel-border-hover);box-shadow:0 2px 4px #0000000d;transform:translateY(-2px)}.available-panel.selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 3px var(--panel-bg-selected)}.available-panel.in-use{opacity:.5;border-style:dashed}.available-panel.in-use:hover{transform:translateY(0);opacity:.6}.panel-label{font-weight:600;color:var(--panel-label-text);font-size:.875rem}.panel-preview{flex:1;display:flex;align-items:center;justify-content:center;padding:.5rem;background:var(--panel-preview-bg);border-radius:4px;font-size:.75rem;color:var(--panel-preview-text)}.selection-hint{padding:.75rem 1rem;background:var(--hint-bg);border:1px solid var(--hint-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}.available-panel.multi-selected{border-color:var(--panel-border-selected);background:var(--panel-bg-selected);box-shadow:0 0 0 2px var(--panel-bg-selected)}.multi-select-badge{margin-left:.5rem;padding:.25rem .5rem;background:var(--panel-border-selected);color:#fff;font-size:.75rem;border-radius:12px;font-weight:400}.multi-select-hint{background:var(--panel-bg-selected);border-color:var(--panel-border-selected)}.slot.tab-group{border-style:solid}.group-content{position:relative;overflow-y:auto;min-height:0}.group-badge{font-size:.75rem;font-weight:600;color:var(--panel-border-selected);margin-bottom:.5rem}.group-panels{display:flex;flex-direction:column;gap:.25rem;flex:1;overflow-y:auto;min-height:0}.group-panel-label{font-size:.75rem;color:var(--slot-content-text);padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.slot-panel-label{font-weight:600;color:var(--slot-content-text);font-size:.875rem;text-align:center}.create-tab-group-btn{margin-top:.5rem;padding:.5rem .75rem;background:var(--panel-border-selected);color:#fff;border:none;border-radius:4px;font-size:.75rem;font-weight:600;cursor:pointer;transition:all .2s ease}.create-tab-group-btn:hover{transform:scale(1.05);box-shadow:0 2px 4px #0000001a}.tab-mode-toggle{position:absolute;top:.5rem;left:.5rem;padding:.25rem .5rem;background:var(--slot-bg);border:1px solid var(--slot-border);border-radius:4px;font-size:.85rem;cursor:pointer;transition:all .2s ease;z-index:10;opacity:.7;color:var(--slot-content-text)}.tab-mode-toggle svg{display:block}.tab-mode-toggle:hover{opacity:1;border-color:var(--slot-border-hover);background:var(--slot-preview-bg)}.tab-mode-toggle.active{opacity:1;background:var(--panel-border-selected);color:#fff;border-color:var(--panel-border-selected)}.tab-config-controls{margin-bottom:.5rem}.tab-config-label{display:flex;align-items:center;gap:.5rem;font-size:.75rem;color:var(--slot-label)}.tab-config-label select{padding:.25rem .5rem;border:1px solid var(--slot-border);border-radius:3px;background:var(--slot-bg);color:var(--slot-content-text);font-size:.7rem;cursor:pointer}.group-panel-item{display:flex;align-items:center;justify-content:space-between;gap:.5rem;padding:.25rem .5rem;background:var(--slot-preview-bg);border-radius:3px;border-left:3px solid var(--panel-border-selected)}.group-panel-label{flex:1;font-size:.75rem;color:var(--slot-content-text);display:flex;align-items:center;gap:.25rem}.default-badge{color:var(--panel-border-selected);font-size:.9em}.remove-from-group-btn{width:18px;height:18px;padding:0;background:var(--clear-btn-bg);color:var(--clear-btn-text);border:none;border-radius:3px;cursor:pointer;font-size:1rem;line-height:1;display:flex;align-items:center;justify-content:center;opacity:.7;transition:all .2s ease}.remove-from-group-btn:hover{opacity:1;transform:scale(1.1)}.usage-hint{padding:.75rem 1rem;background:var(--slot-preview-bg);border:1px solid var(--slot-border);border-radius:6px;color:var(--hint-text);font-size:.875rem;text-align:center}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principal-ade/panel-layouts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Pre-built panel layout components and workspace management for the Panel Framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
"react-dom": ">=18.0.0"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@
|
|
64
|
-
"@
|
|
63
|
+
"@principal-ade/industry-theme": "^0.1.2",
|
|
64
|
+
"@principal-ade/panels": "^1.0.39",
|
|
65
65
|
"react-resizable-panels": "^3.0.0"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|