@pasquelin/panels 0.1.1 → 0.3.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/llms-full.txt CHANGED
@@ -20,14 +20,23 @@
20
20
  provider its own file sits under.
21
21
  - **One store per chassis**, created not imported (`createPanelsStore`). No module singleton —
22
22
  two chassis can live in one application, and a test cannot leak into the next.
23
- - **What is stored is not what is shown.** Stored: which panel each half holds, and the sizes.
24
- Resolved at render: what a zone draws (a `solo` panel silences the other half), the size it
25
- takes (bounded against the opposite zone), whether it takes room at all (the band's two halves
26
- share one height).
27
- - **There are no "surfaces" or sections.** An earlier design carried them; they were removed. In
28
- React the router changes the children there is nothing to solve.
23
+ - **What is stored is not what is shown, and only a CHOICE is stored.** Stored: which panel each
24
+ half was told to hold, per view, and the sizes. A half nobody touched is stored open, naming
25
+ nobody, and a view nobody has opened is simply absent which is what tells the chassis to
26
+ settle it. Resolved at render: which panel a half draws (the one named if it is still declared for
27
+ that half, else the first one that is), whether the zone draws at all, the size it takes
28
+ (bounded against the opposite zone), whether it takes room (the band's two halves share one
29
+ height).
30
+ - **No tooltips, no icon set, no i18n.** The two buttons the chassis draws — the rail's and the
31
+ close — are REPLACED, not configured: `<Panels components={{ IconButton: Mine }}>`. That is
32
+ what keeps `tooltip`, `shortcut` and `badge` from landing here one after the other.
29
33
  - **A panel that should not be offered is a panel you do not declare.** That replaces what a
30
- capability system would be for.
34
+ capability system would be for — and withdrawing one costs nothing: the half falls back, the
35
+ choice is remembered, and declaring the panel again gives it its place back.
36
+ - **Views are named arrangements, and they are opt-in.** `<Panels view="review">`: each view
37
+ keeps the panels it had open to itself, the lengths stay shared. Left out, everything lands in
38
+ one view and nothing about this is visible. Not "surfaces": the library still knows nothing
39
+ about your domain, only that one application can have two screens worth arranging apart.
31
40
 
32
41
  ## Install and shape
33
42
 
@@ -75,23 +84,26 @@ usual missing piece.
75
84
  `PanelHeader` `IconButton` `Separator` `ResizeHandle`
76
85
 
77
86
  ### `<Panels>` props
78
- `store` `storageKey` `storage` `defaultOpen` `header` `footer` `railHeader` `labels` `theme`
79
- `className`
87
+ `store` `storageKey` `storage` `defaultOpen` `view` `header` `footer` `railHeader` `labels`
88
+ `components` `theme` `className`
80
89
 
81
90
  ### `<Panel>` props
82
- `id` `zone` `slot?` `title` `icon?` `actions?` `opens?` `solo?`
91
+ `id` `zone` `slot?` `title` `icon?` `actions?` `opens?` `solo?` `fillActions?`
83
92
 
84
93
  ### Hooks
85
94
  - `usePanels<Id>()` → `{ panels, reveal, close, toggle, isShown, focusedZone, reset }`
86
95
  - `useZone<Id>(zone)` → `{ primary, secondary, draws, size, split, focused }`
87
- - `useArrangement` `useShownIn` `useZoneDraws` `useZoneTakesRoom` `useBandHalves`
96
+ - `useArrangement` `useShownIn` `useShownSpecsIn` `useZoneDraws` `useZoneTakesRoom`
97
+ `useBandHalves`
88
98
  - `useContainerFit(ref)` `usePointerDrag()` `usePanelsState(selector)` `usePanelsActions()`
89
99
 
90
100
  ### Core
91
- `createPanelsStore` `shownIn` `specOf` `zoneDraws` `zoneTakesRoom` `undraggedSizeOf`
101
+ `createPanelsStore` `shownIn` `shownSpecsIn` `openOf` `specOf` `zoneDraws` `zoneTakesRoom`
102
+ `undraggedSizeOf`
92
103
  `fitZoneSize` `fitSplit` `sharedSizes` `sizeKeyOf` `MIN_SIZE` `MIN_CENTER` `MIN_SPLIT`
93
104
  `DEFAULT_SIZES` · `browserStorage` `memoryStorage` `readLayout` `writeLayout` `LAYOUT_VERSION`
94
- · `ZONES` `SLOTS` `BOTTOM_ZONES` `ZONES_BY_SIDE` `isBottom` `isHorizontal` `isLeading` `cx`
105
+ · `ZONES` `SLOTS` `BOTTOM_ZONES` `ZONES_BY_SIDE` `DEFAULT_VIEW` `isBottom` `isHorizontal`
106
+ `isLeading` `cx`
95
107
 
96
108
  ### Optional entry point
97
109
  `@pasquelin/panels/dockview` → `DockviewCenter`, for document tabs. Separate so its weight only
@@ -130,6 +142,14 @@ Gauges: `--pnl-rail` `--pnl-rail-button` `--pnl-rail-inset` `--pnl-gutter` `--pn
130
142
  write, so subscribing to either as an object re-renders on every frame of a drag.
131
143
  - `close(id)` closes THAT panel or nothing. `close(zone, slot)` on the store empties the half
132
144
  whatever stands in it — two panels share a half.
145
+ - `show(id)` on a panel already on screen only FOCUSES it. A half drawing it by declaration
146
+ order has named nobody, and writing the name down would settle for every other view a question
147
+ the click never asked.
148
+ - The `view` prop is CONTROLLED: `setView` on the store is overruled by the next render. It is
149
+ reconciled on every render, not on a dependency change, so the contract does not depend on
150
+ which other props you happened to memoise.
151
+ - Views are never evicted. `view={location.pathname}` grows one entry per URL, for ever. Name
152
+ the handful of screens that own an arrangement, not every route.
133
153
  - `useZoneTakesRoom` ≠ `useZoneDraws`: the band's halves share a height, so either drawing means
134
154
  the strip takes it.
135
155
  - Two chassis in one app need two `storageKey`s, or they overwrite each other.
@@ -309,6 +329,19 @@ For coding agents: [`llms.txt`](llms.txt) is the mental model, the API and the t
309
329
  page; [`llms-full.txt`](llms-full.txt) is every English chapter concatenated. Both ship with the
310
330
  package, and `pnpm llms` regenerates the second from the first plus `docs/`.
311
331
 
332
+ ## Releasing
333
+
334
+ `main` is production; `develop` is where work lands. A release is a tag:
335
+
336
+ ```bash
337
+ npm version patch # or minor / major — bumps, commits, tags
338
+ git push --follow-tags
339
+ ```
340
+
341
+ The tag triggers `release.yml`, which checks that it matches `package.json`, replays
342
+ `pnpm validate`, builds, and publishes to npm with a signed provenance attestation — over OIDC,
343
+ with no token anywhere. See [CHANGELOG.md](CHANGELOG.md) for what shipped.
344
+
312
345
  ## Licence
313
346
 
314
347
  **MIT** — see [LICENSE](LICENSE).
@@ -387,8 +420,8 @@ To open somewhere else, name the halves yourself:
387
420
  <Panels<PanelId> defaultOpen={{ left: { primary: 'search' } }}>
388
421
  ```
389
422
 
390
- `defaultOpen` is read once, and never against a layout that was restored — an arrangement the
391
- reader made outranks a default.
423
+ `defaultOpen` is read once per view, and never against a layout that was restored — an
424
+ arrangement the reader made outranks a default.
392
425
 
393
426
  ## `opens`
394
427
 
@@ -416,11 +449,21 @@ arrangement nobody remembers making, is not a restoration.
416
449
 
417
450
  `solo` only applies to `primary`.
418
451
 
452
+ ## `fillActions`
453
+
454
+ A panel in a horizontal zone gets the header's free width for its actions — right for a montage
455
+ bar, wrong for a band holding a list with two buttons. Say which yours is:
456
+
457
+ ```tsx
458
+ <Panel id="problems" zone="bottomRight" title="Problems" fillActions={false} actions={…}>
459
+ ```
460
+
419
461
  ## Adding and removing panels
420
462
 
421
- The registry follows your JSX. A panel that stops being declared is unregistered, and the half it
422
- occupied is emptied a stored id naming a panel that no longer exists would otherwise leave a
423
- frame with nothing to draw in it.
463
+ The registry follows your JSX, in the order you declare it. A panel that stops being declared
464
+ leaves the rail, and the half it held **falls back** to whatever is still declared for it — it is
465
+ not closed, and the choice is not forgotten. Declare the panel again and the half is its once
466
+ more, in the place it had.
424
467
 
425
468
  Conditional panels work as you would expect:
426
469
 
@@ -431,7 +474,29 @@ Conditional panels work as you would expect:
431
474
  ```
432
475
 
433
476
  This is how you express what a capability system would otherwise be for: a panel that should not
434
- be offered is a panel you do not declare.
477
+ be offered is a panel you do not declare. And because a withdrawal costs nothing, a panel may go
478
+ and come back as often as a right, a route or a connection does.
479
+
480
+ ## Views
481
+
482
+ Two parts of one application may want their own arrangement — an editor and a review screen, a
483
+ project and a dashboard. Name the one in front, and each keeps the panels it had open:
484
+
485
+ ```tsx
486
+ <Panels<PanelId> view={reviewing ? 'review' : 'edit'}>
487
+ ```
488
+
489
+ Closing a column in one view leaves it open in the other, and coming back finds it as it was
490
+ left. The **lengths are shared**: a column that changed width on the way to another view would
491
+ read as another window.
492
+
493
+ Left out, everything lands in one view and nothing about this is visible. Views cost nothing to
494
+ ignore, and a project that grows into a second one only has a prop to pass.
495
+
496
+ The prop is **controlled**: it is reconciled on every render, so `setView` called behind its back
497
+ is taken over on the next one. And views are never evicted — name the handful of screens that own
498
+ an arrangement rather than every route, or `view={location.pathname}` grows one stored entry per
499
+ URL, for ever.
435
500
 
436
501
  ==============================================================================
437
502
  Layout
@@ -519,7 +584,9 @@ This is what lets it be adopted a piece at a time in an application that already
519
584
 
520
585
  ## Persistence
521
586
 
522
- What is stored: which half holds which panel, and the sizes. That is all.
587
+ What is stored: which half holds which panel, in each view, and the sizes. That is all — and
588
+ only a real choice is written: a half nobody has touched is stored as open, naming nobody, so it
589
+ follows your declarations rather than freezing an answer.
523
590
 
524
591
  ```tsx
525
592
  <Panels
@@ -641,16 +708,24 @@ window.electron?.onMenu(id => store.getState().show(id))
641
708
  ```
642
709
 
643
710
  `store.getState()` gives the same actions the hooks call: `show`, `close`, `toggle`, `focus`,
644
- `resize`, `resplit`, `resplitBand`, `fit`, `reset`.
711
+ `resize`, `resplit`, `resplitBand`, `fit`, `reset` — plus `declare` and `setView`, which the
712
+ chassis drives itself.
713
+
714
+ Inside React, the `view` prop is the only way to change views: it is controlled and reconciled on
715
+ every render, so a `setView` made behind its back is taken over on the next one. `usePanelsActions`
716
+ therefore does not offer it — one path per question.
645
717
 
646
718
  ## `usePanelsState`
647
719
 
648
720
  Any slice, subscribed:
649
721
 
650
722
  ```tsx
651
- const open = usePanelsState<PanelId, OpenByZone<PanelId>>(state => state.open)
723
+ const open = usePanelsState<PanelId, OpenByZone<PanelId>>(state => state.views[state.view])
652
724
  ```
653
725
 
726
+ The arrangement of the view on screen — `openOf(state)` says the same thing off a state you
727
+ already hold.
728
+
654
729
  Prefer scalar selectors. `lengths` and `available` are replaced wholesale on every write, so
655
730
  subscribing to either as an object wakes your component on every frame of a drag.
656
731
 
@@ -843,6 +918,22 @@ What the chassis does for you:
843
918
  What is yours: the contrast of your palette, and the accessible names of whatever you put inside
844
919
  the panels.
845
920
 
921
+ ## Drawing the buttons yourself
922
+
923
+ The chassis draws two buttons you never reach: the rail's, and a panel header's close. Replace
924
+ them rather than configure them — a tooltip, a shortcut hint, a badge belong to your design
925
+ system, not to a layout library.
926
+
927
+ ```tsx
928
+ <Panels components={{ IconButton: MyIconButton }}>
929
+ ```
930
+
931
+ `MyIconButton` receives `IconButtonProps`: `icon`, `label`, `active`, `accented`, `acts`,
932
+ `onClick`, plus anything else you spread. `acts` tells the close button from a rail toggle.
933
+
934
+ Read **once**, like `storage`: an object written inline changes identity on every render, and
935
+ every panel would re-render with it.
936
+
846
937
  ==============================================================================
847
938
  Document tabs
848
939
  ==============================================================================
@@ -1008,6 +1099,10 @@ Give them different storage keys, or they overwrite each other's layout:
1008
1099
 
1009
1100
  Each `<Panels>` makes its own store; nothing is shared between them.
1010
1101
 
1102
+ Two chassis is not the same question as two **views**. Two chassis are two frames on screen at
1103
+ once; two views are one frame that two parts of the application arrange differently — see
1104
+ `view` in [PANELS](PANELS.md#views), which keeps the lengths shared.
1105
+
1011
1106
  ## Testing
1012
1107
 
1013
1108
  The chassis is ordinary React. Give it a storage that forgets, so one test cannot leak into the
@@ -1097,17 +1192,25 @@ the same constraint `<Route>` has, and it is understood.
1097
1192
 
1098
1193
  ## What is stored, and what is resolved
1099
1194
 
1100
- Stored: which panel each half holds, and the sizes. Nothing else.
1195
+ Stored: which panel each half was **told** to hold, per view, and the sizes. Nothing else — and
1196
+ only a real choice: a half nobody has touched is stored open, naming nobody.
1101
1197
 
1102
1198
  Everything else is **resolved at render**:
1103
1199
 
1104
- - what a zone *draws* is not what it *holds* — a `solo` panel silences the other half
1200
+ - which panel a half *draws* is not what it *holds* — the one it names if that panel is still
1201
+ declared for that half, else the first one that is
1202
+ - what a zone *draws* is not what its halves hold either — a `solo` panel silences the other
1105
1203
  - the size a zone *takes* is bounded against the opposite zone and the measured room
1106
1204
  - whether a zone *takes room* is not whether it *draws* — the band's two halves share one height
1107
1205
 
1108
1206
  Each of those was a bug before it was a function. They are `shownIn`, `sharedSizes` and
1109
1207
  `zoneTakesRoom`, and each has a test that starts from the arrangement that broke it.
1110
1208
 
1209
+ Resolving the first one at render rather than writing it down is what makes conditional panels
1210
+ free. A panel withdrawn — behind a right, a route, a connection — leaves a half that falls back;
1211
+ declared again, it takes its half back. Written down, the choice would have been lost the first
1212
+ time the panel was hidden, and nothing would ever have restored it.
1213
+
1111
1214
  ## Why the selectors are scalar
1112
1215
 
1113
1216
  `resize`, `resplit` and `fit` replace `lengths` and `available` wholesale. A component subscribed
@@ -1136,11 +1239,16 @@ not something a repaint should reach.
1136
1239
 
1137
1240
  ## What was deliberately left out
1138
1241
 
1139
- - **surfaces / sections** — the chassis has no notion of "which screen am I on". In React the
1140
- router changes the children; there is no problem to solve.
1242
+ - **surfaces / sections** — the chassis still has no notion of what a screen *is*, or of your
1243
+ domain. What it does carry, since views, is that one application can have two screens worth
1244
+ arranging apart: `view` names an arrangement and nothing more. The router changes the children;
1245
+ the view says which arrangement they are drawn in.
1141
1246
  - **capabilities** — a panel that should not be offered is a panel you do not declare.
1142
1247
  - **i18n** — four strings, passed in already translated.
1143
1248
  - **an icon set** — `icon` takes any node.
1249
+ - **tooltips** — and everything else a design system puts on a button. The two buttons the
1250
+ chassis draws itself are replaceable (`components`), which is what keeps `tooltip`, `shortcut`
1251
+ and `badge` from becoming props here one after the other.
1144
1252
  - **floating and dragging panels between zones** — planned, and the state model already allows an
1145
1253
  override of the declared zone. Not in v1.
1146
1254
 
package/llms.txt CHANGED
@@ -20,14 +20,23 @@
20
20
  provider its own file sits under.
21
21
  - **One store per chassis**, created not imported (`createPanelsStore`). No module singleton —
22
22
  two chassis can live in one application, and a test cannot leak into the next.
23
- - **What is stored is not what is shown.** Stored: which panel each half holds, and the sizes.
24
- Resolved at render: what a zone draws (a `solo` panel silences the other half), the size it
25
- takes (bounded against the opposite zone), whether it takes room at all (the band's two halves
26
- share one height).
27
- - **There are no "surfaces" or sections.** An earlier design carried them; they were removed. In
28
- React the router changes the children there is nothing to solve.
23
+ - **What is stored is not what is shown, and only a CHOICE is stored.** Stored: which panel each
24
+ half was told to hold, per view, and the sizes. A half nobody touched is stored open, naming
25
+ nobody, and a view nobody has opened is simply absent which is what tells the chassis to
26
+ settle it. Resolved at render: which panel a half draws (the one named if it is still declared for
27
+ that half, else the first one that is), whether the zone draws at all, the size it takes
28
+ (bounded against the opposite zone), whether it takes room (the band's two halves share one
29
+ height).
30
+ - **No tooltips, no icon set, no i18n.** The two buttons the chassis draws — the rail's and the
31
+ close — are REPLACED, not configured: `<Panels components={{ IconButton: Mine }}>`. That is
32
+ what keeps `tooltip`, `shortcut` and `badge` from landing here one after the other.
29
33
  - **A panel that should not be offered is a panel you do not declare.** That replaces what a
30
- capability system would be for.
34
+ capability system would be for — and withdrawing one costs nothing: the half falls back, the
35
+ choice is remembered, and declaring the panel again gives it its place back.
36
+ - **Views are named arrangements, and they are opt-in.** `<Panels view="review">`: each view
37
+ keeps the panels it had open to itself, the lengths stay shared. Left out, everything lands in
38
+ one view and nothing about this is visible. Not "surfaces": the library still knows nothing
39
+ about your domain, only that one application can have two screens worth arranging apart.
31
40
 
32
41
  ## Install and shape
33
42
 
@@ -75,23 +84,26 @@ usual missing piece.
75
84
  `PanelHeader` `IconButton` `Separator` `ResizeHandle`
76
85
 
77
86
  ### `<Panels>` props
78
- `store` `storageKey` `storage` `defaultOpen` `header` `footer` `railHeader` `labels` `theme`
79
- `className`
87
+ `store` `storageKey` `storage` `defaultOpen` `view` `header` `footer` `railHeader` `labels`
88
+ `components` `theme` `className`
80
89
 
81
90
  ### `<Panel>` props
82
- `id` `zone` `slot?` `title` `icon?` `actions?` `opens?` `solo?`
91
+ `id` `zone` `slot?` `title` `icon?` `actions?` `opens?` `solo?` `fillActions?`
83
92
 
84
93
  ### Hooks
85
94
  - `usePanels<Id>()` → `{ panels, reveal, close, toggle, isShown, focusedZone, reset }`
86
95
  - `useZone<Id>(zone)` → `{ primary, secondary, draws, size, split, focused }`
87
- - `useArrangement` `useShownIn` `useZoneDraws` `useZoneTakesRoom` `useBandHalves`
96
+ - `useArrangement` `useShownIn` `useShownSpecsIn` `useZoneDraws` `useZoneTakesRoom`
97
+ `useBandHalves`
88
98
  - `useContainerFit(ref)` `usePointerDrag()` `usePanelsState(selector)` `usePanelsActions()`
89
99
 
90
100
  ### Core
91
- `createPanelsStore` `shownIn` `specOf` `zoneDraws` `zoneTakesRoom` `undraggedSizeOf`
101
+ `createPanelsStore` `shownIn` `shownSpecsIn` `openOf` `specOf` `zoneDraws` `zoneTakesRoom`
102
+ `undraggedSizeOf`
92
103
  `fitZoneSize` `fitSplit` `sharedSizes` `sizeKeyOf` `MIN_SIZE` `MIN_CENTER` `MIN_SPLIT`
93
104
  `DEFAULT_SIZES` · `browserStorage` `memoryStorage` `readLayout` `writeLayout` `LAYOUT_VERSION`
94
- · `ZONES` `SLOTS` `BOTTOM_ZONES` `ZONES_BY_SIDE` `isBottom` `isHorizontal` `isLeading` `cx`
105
+ · `ZONES` `SLOTS` `BOTTOM_ZONES` `ZONES_BY_SIDE` `DEFAULT_VIEW` `isBottom` `isHorizontal`
106
+ `isLeading` `cx`
95
107
 
96
108
  ### Optional entry point
97
109
  `@pasquelin/panels/dockview` → `DockviewCenter`, for document tabs. Separate so its weight only
@@ -130,6 +142,14 @@ Gauges: `--pnl-rail` `--pnl-rail-button` `--pnl-rail-inset` `--pnl-gutter` `--pn
130
142
  write, so subscribing to either as an object re-renders on every frame of a drag.
131
143
  - `close(id)` closes THAT panel or nothing. `close(zone, slot)` on the store empties the half
132
144
  whatever stands in it — two panels share a half.
145
+ - `show(id)` on a panel already on screen only FOCUSES it. A half drawing it by declaration
146
+ order has named nobody, and writing the name down would settle for every other view a question
147
+ the click never asked.
148
+ - The `view` prop is CONTROLLED: `setView` on the store is overruled by the next render. It is
149
+ reconciled on every render, not on a dependency change, so the contract does not depend on
150
+ which other props you happened to memoise.
151
+ - Views are never evicted. `view={location.pathname}` grows one entry per URL, for ever. Name
152
+ the handful of screens that own an arrangement, not every route.
133
153
  - `useZoneTakesRoom` ≠ `useZoneDraws`: the band's halves share a height, so either drawing means
134
154
  the strip takes it.
135
155
  - Two chassis in one app need two `storageKey`s, or they overwrite each other.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasquelin/panels",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Châssis à panneaux React : rails d'icônes, zones redimensionnables, centre libre. Headless + composants par défaut, entièrement surchargeable.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.16.0",
@@ -24,6 +24,7 @@
24
24
  "files": [
25
25
  "dist",
26
26
  "LICENSE",
27
+ "CHANGELOG.md",
27
28
  "llms.txt",
28
29
  "llms-full.txt"
29
30
  ],
@@ -45,22 +46,24 @@
45
46
  "./package.json": "./package.json"
46
47
  },
47
48
  "scripts": {
48
- "types": "tsc -p tsconfig.build.json",
49
49
  "build": "tsc --noEmit && vite build && pnpm types",
50
+ "types": "tsc -p tsconfig.build.json",
50
51
  "prepack": "pnpm build",
51
52
  "typecheck": "tsc --noEmit",
53
+ "lint": "eslint src examples",
54
+ "format": "prettier --write \"{src,examples,site}/**/*.{ts,tsx,css}\"",
55
+ "format:check": "prettier --check \"{src,examples,site}/**/*.{ts,tsx,css}\"",
52
56
  "test": "vitest run",
53
57
  "test:watch": "vitest",
54
- "lint": "eslint src examples",
55
- "format": "prettier --write \"{src,examples}/**/*.{ts,tsx,css}\"",
56
- "format:check": "prettier --check \"{src,examples}/**/*.{ts,tsx,css}\"",
57
- "dev": "vite --config examples/vite.config.ts",
58
- "build:site": "vite build --config examples/vite.config.ts",
59
- "preview:site": "vite preview --config examples/vite.config.ts",
60
- "llms": "node scripts/make-llms-full.mjs",
61
- "llms:check": "node scripts/make-llms-full.mjs --check",
58
+ "llms": "node scripts/make-llms.mjs",
59
+ "site:check": "node scripts/check-site.mjs",
60
+ "llms:check": "node scripts/make-llms.mjs --check",
62
61
  "licences:check": "node scripts/check-licences.mjs",
63
- "validate": "pnpm llms:check && pnpm licences:check && pnpm typecheck && pnpm lint && pnpm format:check && pnpm test"
62
+ "validate": "pnpm site:check && pnpm llms:check && pnpm licences:check && pnpm typecheck && pnpm lint && pnpm format:check && pnpm test",
63
+ "dev:example": "vite --config examples/vite.config.ts",
64
+ "build:example": "vite build --config examples/vite.config.ts",
65
+ "build:site": "pnpm build:example && node site/build.mjs _site",
66
+ "preview:site": "node scripts/serve.mjs _site --base=/panels/"
64
67
  },
65
68
  "peerDependencies": {
66
69
  "dockview-react": ">=8.0.0",