react-dockable-desktop 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,28 +1,33 @@
1
1
  # React Dockable Desktop
2
2
 
3
- [![npm version](https://img.shields.io/badge/npm-v2.0.0-blue.svg)](https://www.npmjs.com/package/react-dockable-desktop)
3
+ [![npm version](https://img.shields.io/badge/npm-v3.1.0-blue.svg)](https://www.npmjs.com/package/react-dockable-desktop)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-first-3178c6.svg)](https://www.typescriptlang.org/)
5
+ [![Touch Ready](https://img.shields.io/badge/touch-iPad%20%7C%20Android-success.svg)](#touch--mobile)
4
6
  [![license](https://img.shields.io/badge/license-MIT-green.svg)](#license)
5
7
  [![Demo](https://img.shields.io/badge/demo-live-brightgreen.svg)](https://felipecarrillo100.github.io/react-dockable-desktop/demo/)
6
8
  [![Docs](https://img.shields.io/badge/docs-site-blue.svg)](https://felipecarrillo100.github.io/react-dockable-desktop/)
7
9
 
8
- A premium React window manager and dockable layout engine. Fluid split-docking grids, resizable floating windows, dynamic taskbars, and tabbed panels with **zero-unmount DOM persistence** and **built-in i18n/RTL support**.
10
+ A premium dockable layout engine for React. Build desktop-class applications with fluid split-docking grids, tabbed panels, resizable floating windows, and **zero-unmount DOM preservation** — so WebGL contexts, live maps, and stateful editors never lose their state when moved or re-tabbed.
9
11
 
10
- **[Full Documentation](https://felipecarrillo100.github.io/react-dockable-desktop/)**  | 
11
12
  **[Live Demo](https://felipecarrillo100.github.io/react-dockable-desktop/demo/)**  | 
13
+ **[Full Documentation](https://felipecarrillo100.github.io/react-dockable-desktop/)**  | 
12
14
  **[API Reference](https://felipecarrillo100.github.io/react-dockable-desktop/api/)**
13
15
 
14
16
  ---
15
17
 
16
- ## Key Features
18
+ ## Features
17
19
 
18
- - **Dockable Splits & Tab Grid** — drag panels to split screens or group them into tabbed containers
19
- - **Workspace Edge Docking** — drag to outer edges to dock as full-width/height columns or rows
20
- - **Floating Windows** — pop panels into resizable floating windows with maximize and minimize
21
- - **Zero-Unmount DOM Persistence** — WebGL, maps, terminals, and stateful forms keep their DOM node and state when moved
22
- - **i18n & RTL** — full Right-to-Left layout support with automatic `dir="rtl"` detection
23
- - **Inter-Panel Pub/Sub** — decoupled lightweight messaging between active panels
24
- - **Imperative API** — `WorkspaceClient` lets you open, close, focus, and serialize panels from anywhere outside React
25
- - **Layout Serialization** — save/restore the full workspace layout as a JSON string
20
+ - **Split-Docking Grid** — drag panels to split any zone into rows/columns or group into tabbed containers
21
+ - **Workspace Edge Docking** — drag to the outer edges to dock a panel as a full-width or full-height strip
22
+ - **Floating Windows** — pop panels into freely resizable floating windows; 8-direction resize handles (N/NE/E/SE/S/SW/W/NW), maximize, minimize
23
+ - **Touch & Mobile Ready** — full iPad and Android support: long-press to drag tabs, touch resize, 44px coarse-pointer targets throughout
24
+ - **Zero-Unmount DOM Persistence** — panel DOM nodes are moved, never destroyed; WebGL, maps, terminals, and forms retain full state
25
+ - **i18n & RTL** — full Right-to-Left layout support; `dir="rtl"` flips every control, tab order, and drop zone automatically
26
+ - **Inter-Panel Pub/Sub** — lightweight typed event bus for decoupled panel-to-panel communication
27
+ - **Imperative API** — `WorkspaceClient` opens, closes, focuses, and serializes panels from anywhere — inside or outside React
28
+ - **Layout Serialization** — save and restore the full workspace as a JSON string; survives page reloads
29
+ - **7 Built-in Skins** — VSCode, macOS, Chrome, Slate, Nord, Obsidian, Tokyo — all fully themeable via CSS variables
30
+ - **TypeScript-first** — complete type definitions included; no separate `@types/` package needed
26
31
 
27
32
  ---
28
33
 
@@ -32,28 +37,33 @@ A premium React window manager and dockable layout engine. Fluid split-docking g
32
37
  npm install react-dockable-desktop replace-react-contexify
33
38
  ```
34
39
 
35
- Import styles in your entry file:
40
+ Import styles in your app entry file. **Order matters** — the contexify sheet must come first:
36
41
 
37
42
  ```ts
38
43
  import 'replace-react-contexify/styles.css';
39
44
  import 'react-dockable-desktop/styles.css';
40
45
  ```
41
46
 
47
+ **Requirements:** React ≥ 16.8 · Node ≥ 18
48
+
42
49
  ---
43
50
 
44
51
  ## Quick Start
45
52
 
46
53
  ### 1. Create a WorkspaceClient
47
54
 
55
+ Define your panel catalog and create a `WorkspaceClient` **outside React**, at module scope. It acts as the bridge between your imperative code and the React tree.
56
+
48
57
  ```ts
58
+ // workspace.ts
49
59
  import { WorkspaceClient } from 'react-dockable-desktop';
50
- import MapPanel from './panels/MapPanel';
60
+ import MapPanel from './panels/MapPanel';
51
61
  import EditorPanel from './panels/EditorPanel';
52
62
 
53
- export const client = new WorkspaceClient({
63
+ export const workspace = new WorkspaceClient({
54
64
  panels: {
55
65
  map: { component: MapPanel, defaultOptions: { title: 'Map View' } },
56
- editor: { component: EditorPanel, defaultOptions: { title: 'Editor' } },
66
+ editor: { component: EditorPanel, defaultOptions: { title: 'Editor' } },
57
67
  },
58
68
  initialState: localStorage.getItem('workspace-layout'),
59
69
  });
@@ -61,97 +71,308 @@ export const client = new WorkspaceClient({
61
71
 
62
72
  ### 2. Mount the Provider
63
73
 
74
+ `DockableDesktopProvider` is the single composite provider — it wraps everything the library needs:
75
+
64
76
  ```tsx
65
- import { WindowManagerProvider, WindowManager, PanelProvider, ModalStackRenderer, SidePanelRenderer } from 'react-dockable-desktop';
66
- import { client } from './workspaceClient';
77
+ // App.tsx
78
+ import { DockableDesktopProvider, WindowManager, ModalStackRenderer } from 'react-dockable-desktop';
79
+ import { workspace } from './workspace';
67
80
 
68
- function App() {
81
+ export default function App() {
69
82
  return (
70
- <WindowManagerProvider client={client}>
71
- <PanelProvider>
72
- <div style={{ width: '100vw', height: '100vh' }}>
73
- <WindowManager />
74
- </div>
75
- <ModalStackRenderer />
76
- <SidePanelRenderer />
77
- </PanelProvider>
78
- </WindowManagerProvider>
83
+ <DockableDesktopProvider client={workspace}>
84
+ <div style={{ width: '100vw', height: '100vh' }}>
85
+ <WindowManager />
86
+ </div>
87
+ <ModalStackRenderer />
88
+ </DockableDesktopProvider>
79
89
  );
80
90
  }
81
91
  ```
82
92
 
83
- ### 3. Open Panels Imperatively
93
+ > **Important:** the `WindowManager` container must have an explicit height. A `height: 100%` that resolves to zero will produce a development warning.
94
+
95
+ ### 3. Open Panels
84
96
 
85
97
  ```ts
86
- // From anywhere outside React:
87
- client.openPanel('map-1', 'map', { title: 'Satellite View' });
88
- client.focusPanel('map-1');
89
- client.saveLayout();
90
-
91
- // Query state:
92
- client.isOpen('map-1'); // boolean
93
- client.getOpenPanelIds(); // string[]
98
+ // From anywhere — inside or outside React:
99
+ workspace.openPanel('map-1', 'map');
100
+ workspace.openPanel('ed-1', 'editor', { title: 'config.json', initialTarget: 'floating' });
101
+ workspace.focusPanel('map-1');
102
+
103
+ // Layout persistence:
104
+ localStorage.setItem('workspace-layout', workspace.saveLayout());
105
+
106
+ // Query state without a hook:
107
+ workspace.isOpen('map-1'); // boolean
108
+ workspace.getOpenPanelIds(); // string[]
109
+ ```
110
+
111
+ Panels can now be dragged, split, tabbed, floated, and minimized out of the box.
112
+
113
+ ---
114
+
115
+ ## Writing a Panel Component
116
+
117
+ A panel is any React component. Use built-in hooks to integrate with the layout:
118
+
119
+ ```tsx
120
+ import { useFormContainer, usePanelId } from 'react-dockable-desktop';
121
+
122
+ export default function EditorPanel() {
123
+ const panelId = usePanelId(); // this panel's instance ID — no prop needed
124
+ const { setDirty, setTitle } = useFormContainer();
125
+
126
+ const handleChange = (value: string) => {
127
+ setDirty(true); // blocks close until user confirms discard
128
+ setTitle('config.json *'); // updates the tab title live
129
+ };
130
+
131
+ return <textarea onChange={e => handleChange(e.target.value)} />;
132
+ }
94
133
  ```
95
134
 
96
135
  ---
97
136
 
98
137
  ## Hooks
99
138
 
100
- Use these inside any component within the provider tree:
139
+ Call these inside any component within the `DockableDesktopProvider` tree:
101
140
 
102
- | Hook | Returns | Description |
141
+ | Hook | Returns | Use For |
103
142
  | :--- | :--- | :--- |
104
- | `useWindowManagerActions()` | `WindowActions` | Open, close, focus, dock, float, minimize, maximize, serialize panels |
105
- | `useWindowManagerState()` | `WindowState` | Reactive access to grid layout, floating windows, minimized panels |
143
+ | `useWindowManagerActions()` | `WindowActions` | Open, close, float, dock, minimize, maximize, serialize panels |
144
+ | `useWindowManagerState(selector?)` | `WindowState` or selected slice | Read layout, floating windows, active panel ID |
145
+ | `usePanelActions()` | `PanelActions` | Open modal overlays and left/right side drawers |
146
+ | `usePanelContext()` | `{ publish, subscribe }` | Inter-panel typed event bus |
147
+ | `useFormContainer()` | `FormContainerContract` | Dirty state, close guards, dynamic panel title/icon |
148
+ | `usePanelId()` | `string` | The panel's own instance ID — no prop drilling needed |
149
+ | `useToolbar()` | `ToolbarContextValue` | Read/write Toolbar state (active tool, modifiers) from any panel |
150
+ | `useSidebar()` | `SidebarContextValue` | Open/close Sidebar tabs from any component in the Sidebar tree |
151
+ | `useSidebarTab()` | `SidebarTabContext` | Self-control for content inside a Sidebar tab |
152
+ | `usePanelContextMenu(items)` | `void` | Inject dynamic context menu items into this panel's right-click menu |
106
153
  | `useRegistry()` | `PanelRegistryClass` | The scoped panel registry for the current provider |
107
- | `usePanelContext()` | `{ publish, subscribe }` | Inter-panel pub/sub event bus |
108
- | `useFormContainer()` | `FormContainerContract` | Dirty-state tracking, dynamic title overrides, close guards |
109
- | `useFormatMessage()` | `(msg) => string` | Translation formatter matching the provider's i18n config |
154
+ | `useFormatMessage()` | `MessageFormatter` | i18n formatter matching the current provider's locale |
155
+
156
+ **State selectors** prevent unnecessary re-renders:
157
+
158
+ ```ts
159
+ // Only re-renders when activePanelId changes — not on every layout mutation:
160
+ const activeId = useWindowManagerState(s => s.activePanelId);
161
+ const panelCount = useWindowManagerState(s => Object.keys(s.panels).length);
162
+ ```
110
163
 
111
164
  ---
112
165
 
113
- ## v2.0.0 — Breaking Changes
166
+ ## WorkspaceClient Reference
114
167
 
115
- | Removed | Replacement |
116
- | :--- | :--- |
117
- | `bringToFront(id)` | `focusPanel(id)` — works for both floating and docked panels |
118
- | `setActivePanel(id)` on `WindowActions` | `focusPanel(id)` |
168
+ ```ts
169
+ const workspace = new WorkspaceClient({ panels, initialState?, formatMessage?, dir? });
170
+
171
+ // Panel lifecycle
172
+ workspace.openPanel(id, component, options?) // options: title, initialTarget, stickyRight, stickyBottom
173
+ workspace.closePanel(id)
174
+ workspace.focusPanel(id) // raises floating / selects tab for docked
175
+ workspace.floatPanel(id, rect?) // detach to a floating window
176
+ workspace.dockPanel(id) // return floating to the grid
177
+ workspace.minimizePanel(id)
178
+ workspace.restorePanel(id)
179
+ workspace.maximizePanel(id)
180
+
181
+ // Synchronous state queries (no hook needed)
182
+ workspace.isOpen(id) // → boolean
183
+ workspace.getOpenPanelIds() // → string[]
184
+
185
+ // Layout persistence
186
+ workspace.saveLayout() // → JSON string
187
+ workspace.loadLayout(json) // → boolean (true = success)
188
+
189
+ // Event bus
190
+ workspace.publish(event, data)
191
+ workspace.subscribe(event, callback) // → unsubscribe()
192
+ workspace.onPanelOpen(cb)
193
+ workspace.onPanelClose(cb)
194
+ workspace.onPanelMinimize(cb)
195
+ workspace.onPanelRestore(cb)
196
+
197
+ // Direction
198
+ workspace.setDirection('ltr' | 'rtl')
199
+ ```
200
+
201
+ ---
119
202
 
120
- **New in v2.0.0:**
121
- - `focusPanel(id)` — unified "show this panel" method
122
- - `isOpen(id): boolean` — synchronous panel state query
123
- - `getOpenPanelIds(): string[]` list all open panel IDs
124
- - Pending-call queue: imperative calls before the provider mounts are automatically buffered and replayed
125
- - DEV-mode warning if `client=` prop is missing on the provider
126
- - DEV-mode warning if `replace-react-contexify` CSS is not detected
203
+ ## Layout Persistence
204
+
205
+ ```ts
206
+ // Save on unload (or on any meaningful user action):
207
+ window.addEventListener('beforeunload', () => {
208
+ localStorage.setItem('workspace-layout', workspace.saveLayout());
209
+ });
127
210
 
128
- See the [Migration Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration) for the full list of changes.
211
+ // Restore by passing the saved string to the constructor:
212
+ new WorkspaceClient({
213
+ panels: { ... },
214
+ initialState: localStorage.getItem('workspace-layout'),
215
+ });
216
+ ```
129
217
 
130
218
  ---
131
219
 
132
- ## Demo Environments
220
+ ## Side Panels & Modals
133
221
 
134
- ```bash
135
- npm run dev # Leaflet + Monaco open-source demo
136
- npm run dev:ria # LuciadRIA 3D Earth demo (requires license)
222
+ Add `SidePanelRenderer` and `ModalStackRenderer` to your app root. Placement matters — `SidePanelRenderer` must be **inside** the workspace container so drawers position correctly; `ModalStackRenderer` goes **outside** as a full-screen overlay:
223
+
224
+ ```tsx
225
+ // App.tsx
226
+ import { SidePanelRenderer, ModalStackRenderer } from 'react-dockable-desktop';
227
+
228
+ function App() {
229
+ return (
230
+ <DockableDesktopProvider client={workspace}>
231
+ <div style={{ width: '100vw', height: '100vh', overflow: 'hidden', position: 'relative' }}>
232
+ <WindowManager />
233
+ <SidePanelRenderer /> {/* inside — drawers position relative to this container */}
234
+ </div>
235
+ <ModalStackRenderer /> {/* outside — full-screen overlay */}
236
+ </DockableDesktopProvider>
237
+ );
238
+ }
239
+
240
+ // From any panel component:
241
+ const { openModal, openRightPanel } = usePanelActions();
242
+
243
+ openModal(MyForm, { itemId: 42 }, { title: 'Edit Item', size: 'medium' });
244
+ openRightPanel(PropertiesPanel, { nodeId }, { title: 'Properties', width: 320 });
137
245
  ```
138
246
 
139
247
  ---
140
248
 
249
+ ## Touch & Mobile
250
+
251
+ Touch support is built in for v3.1.0+. No extra setup required:
252
+
253
+ - **Tab drag** — long-press (300ms) on any tab to start dragging; haptic feedback on supported devices
254
+ - **Floating window drag** — long-press the titlebar, then drag
255
+ - **Resize** — drag any of the 8 resize handles; minimum 44px touch targets throughout
256
+ - **Split resizer** — drag the 1px divider line; the hit area extends into the safe direction to avoid accidental tab activation
257
+ - **Tab bar scroll** — swipe horizontally in the tab strip to scroll when there are many tabs
258
+
259
+ ---
260
+
261
+ ## i18n & RTL
262
+
263
+ ```tsx
264
+ <DockableDesktopProvider
265
+ dir="rtl"
266
+ formatMessage={(msg) => intl.formatMessage({ id: msg.id, defaultMessage: msg.defaultMessage })}
267
+ predefinedMessages={customMessages}
268
+ >
269
+ ```
270
+
271
+ `dir` can be `'ltr'` (default) or `'rtl'`. All layout, split directions, tab ordering, drop zones, and context menus flip automatically. You can also switch at runtime:
272
+
273
+ ```ts
274
+ workspace.setDirection('rtl');
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Skins
280
+
281
+ ```tsx
282
+ <WindowManager skin="vscode" /> // default
283
+ <WindowManager skin="macos" />
284
+ <WindowManager skin="nord" />
285
+ <WindowManager skin="tokyo" />
286
+ ```
287
+
288
+ | Skin | Character | Active state (Sidebar & Toolbar) |
289
+ |------|-----------|----------------------------------|
290
+ | `vscode` | VS Code dark (default) | Transparent fill, 2 px accent bar |
291
+ | `macos` | Glass Chip — accent fill, rounded corners | 36 px floating chip, white inner ring |
292
+ | `chrome` | Google Chrome tab geometry | Sidebar: half-pill bridge. Toolbar: 2 px bar |
293
+ | `slate` | Fluent Slate — deep navy | Floating 36 px accent-tinted pill |
294
+ | `nord` | Arctic Frost — muted Nord palette | Short horizontal line below icon |
295
+ | `obsidian` | Vercel Midnight — pure black/white | Deep glow + icon drop-shadow |
296
+ | `tokyo` | Tokyo Night — purple accent | Neon glow + vivid icon drop-shadow |
297
+
298
+ All built-in skins include dark and light variants. Create your own skin by overriding CSS custom properties under a `[data-workspace-skin="myskin"]` selector. See the [Theming Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/theming) for the full variable reference and the [Per-skin active state guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/theming#per-skin-active-state-design-language) to customise the Sidebar/Toolbar active indicator in your own skin.
299
+
300
+ ---
301
+
302
+ ## What's New
303
+
304
+ ### v3.2.0
305
+ - **Per-skin active state design language** — Sidebar tabs and Toolbar buttons now use a per-skin visual pattern (transparent bar, floating chip, pill, line, neon glow), driven by new CSS design tokens — fully overridable in custom skins. CSS-only, no API changes.
306
+ - **Documentation overhaul** — All guides updated to cover the full v3.1.0 API surface.
307
+
308
+ ### v3.1.0
309
+ - **`<Toolbar>` component** — Vertical/horizontal strip hosting `action`, `radio`, `toggle`, `group`, and `separator` items. `useToolbar()` reads/writes state from any panel. `ToolbarGroupItem` adds a collapsed tool-family flyout with controlled mode support.
310
+ - **`<WindowManager taskbarVisibility>`** — Three modes: `'always'` (permanent bar, new default), `'compact'` (shows only with minimized panels), `'autohide'` (overlay bar with 8 px peek strip).
311
+ - **Sidebar resizable drawer** — Drag the drawer edge to resize. Props: `defaultWidth` (px), `minWidth`, `maxWidth`, `onWidthChange`. `drawerWidth` (string) deprecated.
312
+ - **Sidebar `visible` / `stripVisible`** — `visible` collapses the entire sidebar; `stripVisible` collapses only the activity bar. New handle methods: `showStrip()`, `hideStrip()`, `setWidth(px)`, `getWidth()`.
313
+ - **`useSidebar()` / `useSidebarTab()` hooks** — Programmatic Sidebar control from any component in the tree.
314
+ - **`usePanelContextMenu()` hook** — Inject dynamic right-click context menu items from inside a panel component.
315
+ - **Touch & iPad/Android support** — Pointer Events migration; long-press (300 ms) activates tab drag; taskbar chips support hover preview and long-press context menu on touch.
316
+ - **8-direction resize handles** — Floating windows now have N, NE, E, SE, S, SW, W, NW resize handles.
317
+ - **Skin scope fix** — `data-workspace-skin` now applied to `document.documentElement` so Toolbar and Sidebar always inherit the correct skin.
318
+
319
+ ### v3.0.0
320
+ - **`DockableDesktopProvider`** — single composite provider replaces the manual `WindowManagerProvider + PanelProvider` nesting
321
+ - **RTL support** — `dir` prop on provider; `setDirection()` on client; full mirroring of all controls
322
+ - **State selectors** — `useWindowManagerState(s => s.activePanelId)` for surgical re-renders
323
+ - **Dynamic ModalStack** — clean overlay system with dirty-state close guards
324
+ - **`usePanelId()` hook** — zero-prop-drilling panel identity
325
+ - **Typed event bus** — `WorkspaceClient<AppEvents>` for type-safe inter-panel messaging
326
+
327
+ ### v2.0.0 — Breaking Changes
328
+
329
+ | Removed | Replacement |
330
+ | :--- | :--- |
331
+ | `bringToFront(id)` | `focusPanel(id)` — works for both floating and docked panels |
332
+ | `setActivePanel(id)` | `focusPanel(id)` |
333
+
334
+ Full details in the [Migration Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration).
335
+
336
+ ---
337
+
141
338
  ## Documentation
142
339
 
143
- Full narrative guides, API reference, and the interactive demo are published at:
340
+ Complete guides, API reference, and interactive demo at:
341
+
342
+ **[https://felipecarrillo100.github.io/react-dockable-desktop/](https://felipecarrillo100.github.io/react-dockable-desktop/)**
144
343
 
145
- **https://felipecarrillo100.github.io/react-dockable-desktop/**
344
+ | Guide | Description |
345
+ | :--- | :--- |
346
+ | [Installation](https://felipecarrillo100.github.io/react-dockable-desktop/guide/installation) | Requirements, CSS import order, module formats |
347
+ | [Quick Start](https://felipecarrillo100.github.io/react-dockable-desktop/guide/quick-start) | Minimal working app with layout persistence |
348
+ | [WorkspaceClient](https://felipecarrillo100.github.io/react-dockable-desktop/guide/workspace-client) | Full imperative API, multiple providers, i18n config |
349
+ | [Panel Registry](https://felipecarrillo100.github.io/react-dockable-desktop/guide/panel-registry) | `defaultOptions`, scoped vs global registry |
350
+ | [Layout System](https://felipecarrillo100.github.io/react-dockable-desktop/guide/layout) | Opening, floating, minimizing, serializing layouts |
351
+ | [Panel Lifecycle & Forms](https://felipecarrillo100.github.io/react-dockable-desktop/guide/forms-and-panels) | Dirty state, close guards, `useFormContainer` |
352
+ | [Modals & Side Panels](https://felipecarrillo100.github.io/react-dockable-desktop/guide/modals-and-drawers) | Modal stack, drawers, `Sidebar` component |
353
+ | [Event Bus](https://felipecarrillo100.github.io/react-dockable-desktop/guide/event-bus) | Typed pub/sub, built-in lifecycle events |
354
+ | [Theming](https://felipecarrillo100.github.io/react-dockable-desktop/guide/theming) | CSS variables, custom skins, dark/light modes |
355
+ | [Advanced Topics](https://felipecarrillo100.github.io/react-dockable-desktop/guide/advanced) | RTL, multiple workspaces, custom header actions |
356
+ | [Best Practices](https://felipecarrillo100.github.io/react-dockable-desktop/guide/best-practices) | Patterns for production-ready implementations |
357
+ | [Migration Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration) | Upgrade from v1 → v2 → v3 |
358
+ | [API Reference](https://felipecarrillo100.github.io/react-dockable-desktop/api/) | Full type-level reference for all exports |
359
+
360
+ ---
146
361
 
147
- - [Getting Started](https://felipecarrillo100.github.io/react-dockable-desktop/guide/)
148
- - [WorkspaceClient Guide](https://felipecarrillo100.github.io/react-dockable-desktop/guide/workspace-client)
149
- - [Layout System](https://felipecarrillo100.github.io/react-dockable-desktop/guide/layout)
150
- - [API Reference](https://felipecarrillo100.github.io/react-dockable-desktop/api/)
151
- - [Migration v1 → v2](https://felipecarrillo100.github.io/react-dockable-desktop/guide/migration)
362
+ ## Development
363
+
364
+ ```bash
365
+ git clone https://github.com/felipecarrillo100/react-dockable-desktop.git
366
+ cd react-dockable-desktop
367
+ npm install
368
+ npm run dev # Leaflet + Monaco open-source demo
369
+ npm run dev:ria # LuciadRIA 3D Earth demo (requires license)
370
+ npm test # vitest unit suite
371
+ npm run build # build dist/
372
+ ```
152
373
 
153
374
  ---
154
375
 
155
376
  ## License
156
377
 
157
- MIT. Free to use, adapt, and build upon.
378
+ MIT free to use, adapt, and build upon.