cursedbelt 5.99.5 → 5.99.6

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.
@@ -24,7 +24,10 @@ export interface PanelGridLeaf {
24
24
  /** A split — lays its children out along `direction` with dividers between them. */
25
25
  export interface PanelGridSplit {
26
26
  type: 'split';
27
- /** Unique within the whole grid — namespaces the persisted layout of this group. */
27
+ /**
28
+ * Unique within the whole grid — namespaces the persisted layout of this group. It need not be
29
+ * unique on the PAGE: the library's group is registered as `<this grid's useId()>:<id>`.
30
+ */
28
31
  id: string;
29
32
  /** 'horizontal' = children side-by-side; 'vertical' = children stacked. */
30
33
  direction: 'horizontal' | 'vertical';
@@ -1 +1 @@
1
- {"version":3,"file":"PanelGrid.d.ts","sourceRoot":"","sources":["../../../src/react/components/PanelGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAW,MAAM,OAAO,CAAC;AAIhD,OAAO,EAAkB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,SAAS,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,SAAS,EAAE,YAAY,GAAG,UAAU,CAAC;IACrC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IACzC,2GAA2G;IAC3G,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,IAAI,EAAE,cAAc,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IACzC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,WAAkB,EAClB,gBAAyB,EACzB,WAAkB,EAClB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,cAAc,+BA2BhB"}
1
+ {"version":3,"file":"PanelGrid.d.ts","sourceRoot":"","sources":["../../../src/react/components/PanelGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAkB,MAAM,OAAO,CAAC;AAIvD,OAAO,EAAkB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,SAAS,CAAC;IACnB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,oFAAoF;AACpF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,2EAA2E;IAC3E,SAAS,EAAE,YAAY,GAAG,UAAU,CAAC;IACrC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IACzC,2GAA2G;IAC3G,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,cAAc,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,+EAA+E;IAC/E,IAAI,EAAE,cAAc,CAAC;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IACzC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,WAAkB,EAClB,gBAAyB,EACzB,WAAkB,EAClB,SAAS,EACT,GAAG,IAAI,EACR,EAAE,cAAc,+BAoChB"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useMemo } from 'react';
2
+ import { useId, useMemo } from 'react';
3
3
  import { Group, Panel, useDefaultLayout } from 'react-resizable-panels';
4
4
  import { useMediaQuery } from '../hooks/useMediaQuery.js';
5
5
  import { cn } from '../lib/cn.js';
@@ -52,33 +52,41 @@ function leavesOf(node) {
52
52
  export function PanelGrid({ root, storageKey, disabled, withHandles = true, separatorVariant = 'line', mobileStack = true, className, ...rest }) {
53
53
  const isMobile = useMediaQuery('(max-width: 639px)');
54
54
  const flatLeaves = useMemo(() => leavesOf(root), [root]);
55
+ // 🔴 react-resizable-panels keys its group registry by `id` ACROSS THE DOCUMENT, and each Group's
56
+ // listener takes every change made under its id. `SplitPane` gave every instance the same root
57
+ // id, `'root'`, so two on one page heard each other's layouts: a collapsible panel looked itself
58
+ // up in the other group's layout, found nothing, and `toFixed` threw out of a ResizeObserver —
59
+ // before the listener applied the new layout (forge task 3981, caught by the kitchen sink's
60
+ // `test:browser:smoke`). It also put a second `id="root"` in the DOM beside the app's mount node.
61
+ // Each grid instance scopes its groups; the persisted key stays `${storageKey}:${split.id}`.
62
+ const scope = useId();
55
63
  if (mobileStack && isMobile) {
56
64
  return (_jsx("div", { ...rest, className: cn('flex flex-col', className), children: flatLeaves.map((leaf) => (_jsx("div", { className: cn('min-h-0', leaf.className), children: leaf.content }, leaf.id))) }));
57
65
  }
58
- return (_jsx(GridSplit, { ...rest, node: root, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant, className: cn('h-full', className) }));
66
+ return (_jsx(GridSplit, { ...rest, node: root, scope: scope, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant, className: cn('h-full', className) }));
59
67
  }
60
68
  function GridSplit(props) {
61
69
  // Persistence needs the useDefaultLayout hook; hooks can't be conditional,
62
70
  // so the persistent and plain variants are separate components.
63
71
  return props.storageKey ? _jsx(PersistentGridSplit, { ...props }) : _jsx(PlainGridSplit, { ...props });
64
72
  }
65
- function PlainGridSplit({ node, storageKey, disabled, withHandles, separatorVariant, className, }) {
66
- return (_jsx(Group, { id: node.id, orientation: node.direction, disabled: disabled, className: cn('flex', node.direction === 'horizontal' ? 'flex-row' : 'flex-col', className ?? node.className), children: _jsx(GridChildren, { node: node, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant }) }));
73
+ function PlainGridSplit({ node, scope, storageKey, disabled, withHandles, separatorVariant, className, }) {
74
+ return (_jsx(Group, { id: `${scope}:${node.id}`, orientation: node.direction, disabled: disabled, className: cn('flex', node.direction === 'horizontal' ? 'flex-row' : 'flex-col', className ?? node.className), children: _jsx(GridChildren, { node: node, scope: scope, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant }) }));
67
75
  }
68
- function PersistentGridSplit({ node, storageKey, disabled, withHandles, separatorVariant, className, }) {
76
+ function PersistentGridSplit({ node, scope, storageKey, disabled, withHandles, separatorVariant, className, }) {
69
77
  const { defaultLayout, onLayoutChanged } = useDefaultLayout({
70
78
  id: `${storageKey}:${node.id}`,
71
79
  onlySaveAfterUserInteractions: true,
72
80
  storage: localStorage,
73
81
  });
74
- return (_jsx(Group, { id: node.id, orientation: node.direction, defaultLayout: defaultLayout, onLayoutChanged: onLayoutChanged, disabled: disabled, className: cn('flex', node.direction === 'horizontal' ? 'flex-row' : 'flex-col', className ?? node.className), children: _jsx(GridChildren, { node: node, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant }) }));
82
+ return (_jsx(Group, { id: `${scope}:${node.id}`, orientation: node.direction, defaultLayout: defaultLayout, onLayoutChanged: onLayoutChanged, disabled: disabled, className: cn('flex', node.direction === 'horizontal' ? 'flex-row' : 'flex-col', className ?? node.className), children: _jsx(GridChildren, { node: node, scope: scope, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant }) }));
75
83
  }
76
- function GridChildren({ node, storageKey, disabled, withHandles, separatorVariant, }) {
84
+ function GridChildren({ node, scope, storageKey, disabled, withHandles, separatorVariant, }) {
77
85
  const showSeparators = node.resizable !== false;
78
86
  const variant = node.separatorVariant ?? separatorVariant;
79
- return (_jsx(_Fragment, { children: node.children.map((child, i) => (_jsx(GridCell, { child: child, direction: node.direction, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: variant, separatorBefore: i > 0 && showSeparators }, child.id))) }));
87
+ return (_jsx(_Fragment, { children: node.children.map((child, i) => (_jsx(GridCell, { child: child, scope: scope, direction: node.direction, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: variant, separatorBefore: i > 0 && showSeparators }, child.id))) }));
80
88
  }
81
- function GridCell({ child, direction, storageKey, disabled, withHandles, separatorVariant, separatorBefore, }) {
82
- return (_jsxs(_Fragment, { children: [separatorBefore && (_jsx(PanelSeparator, { direction: direction, disabled: disabled, variant: separatorVariant, withHandle: withHandles })), _jsx(Panel, { id: child.id, defaultSize: child.defaultSize, minSize: child.minSize, collapsible: true, collapsedSize: 0, className: cn('min-h-0 min-w-0', child.className), children: isSplit(child) ? (_jsx(GridSplit, { node: child, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant, className: 'h-full' })) : (child.content) })] }));
89
+ function GridCell({ child, scope, direction, storageKey, disabled, withHandles, separatorVariant, separatorBefore, }) {
90
+ return (_jsxs(_Fragment, { children: [separatorBefore && (_jsx(PanelSeparator, { direction: direction, disabled: disabled, variant: separatorVariant, withHandle: withHandles })), _jsx(Panel, { id: child.id, defaultSize: child.defaultSize, minSize: child.minSize, collapsible: true, collapsedSize: 0, className: cn('min-h-0 min-w-0', child.className), children: isSplit(child) ? (_jsx(GridSplit, { node: child, scope: scope, storageKey: storageKey, disabled: disabled, withHandles: withHandles, separatorVariant: separatorVariant, className: 'h-full' })) : (child.content) })] }));
83
91
  }
84
92
  //# sourceMappingURL=PanelGrid.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PanelGrid.js","sourceRoot":"","sources":["../../../src/react/components/PanelGrid.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PanelGrid.js","sourceRoot":"","sources":["../../../src/react/components/PanelGrid.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedbelt",
3
- "version": "5.99.5",
3
+ "version": "5.99.6",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
@@ -90,6 +90,7 @@
90
90
  "demo:serve": "bun run demo:build && bun run scripts/demoServer.ts",
91
91
  "build": "rm -rf dist.next dist.prev && bun run scripts/generateStaticStyles.ts && bun run scripts/generateUtilityStyles.ts && bun run scripts/generateAreaStyles.ts && tsc --project tsconfig.build.json --outDir dist.next && tsc --project tsconfig.scripts.build.json --outDir dist.next/scripts && CURSEDBELT_DIST_DIR=dist.next bun run scripts/copyDistAssets.ts && CURSEDBELT_DIST_DIR=dist.next bun run scripts/checkDistExports.ts &&{ [ -d dist ] && mv dist dist.prev || true; } && mv dist.next dist && rm -rf dist.prev",
92
92
  "e2e": "playwright test --config e2e/playwright.config.ts",
93
+ "test:browser:smoke": "playwright test --config e2e/playwright.config.ts smoke.spec.ts --project=chromium",
93
94
  "e2e:filter-rail": "playwright test --config e2e/playwright.config.ts filter-rail.spec.ts",
94
95
  "e2e:table-layout": "playwright test --config e2e/playwright.config.ts table-layout.spec.ts",
95
96
  "e2e:stage-layout": "playwright test --config e2e/playwright.config.ts stage-layout.spec.ts",
@@ -965,7 +966,7 @@
965
966
  "@vidstack/react": "1.15.6",
966
967
  "@vitejs/plugin-react": "^5.2.0",
967
968
  "cursedbelt-server": "^4.43.1",
968
- "cursedops": "^0.22.0",
969
+ "cursedops": "^0.23.0",
969
970
  "diff": "^9.0.0",
970
971
  "emoji-mart": "^5.6.0",
971
972
  "hls.js": "^1.5.0",
@@ -1076,12 +1077,21 @@
1076
1077
  "why": "stylesStaticMatches / stylesUtilitiesMatches read those two files and assert they are current",
1077
1078
  "timeoutSec": 300
1078
1079
  },
1080
+ {
1081
+ "script": "test:browser:smoke",
1082
+ "needs": [
1083
+ "styles"
1084
+ ],
1085
+ "why": "the one browser proof every verify keeps (forge task 3981): the built kitchen sink boots, renders its first sections and logs no console error. Behind styles because the demo bundle is compiled from those stylesheets"
1086
+ }
1087
+ ],
1088
+ "deferred": [
1079
1089
  {
1080
1090
  "script": "e2e",
1081
1091
  "needs": [
1082
1092
  "styles"
1083
1093
  ],
1084
- "why": "the demo bundle it drives is compiled from the same stylesheets"
1094
+ "why": "the whole kitchen-sink suite — 60 specs, ~127 s, the slowest stage in the generation. The queue's suite job runs it after landing (verify --full here), and prepublishOnly's --force runs it before any publish. Behind styles because the demo bundle it drives is compiled from the same stylesheets"
1085
1095
  }
1086
1096
  ]
1087
1097
  }
@@ -14,8 +14,11 @@ import {
14
14
  } from '../demo/devServer.js';
15
15
  import { DEMO_TSCONFIG, derivePaths, readDemoTsconfig, renderPathsBlock } from '../demo/generatePaths.js';
16
16
  import pkg from '../package.json';
17
- // The declared graph, read as data — there is no scheduler in this repo to import from.
18
- const STAGES: readonly { script: string }[] = (pkg as { gate?: { stages?: { script: string }[] } }).gate?.stages ?? [];
17
+ // The declared graph, read as data — there is no scheduler in this repo to import from. Both halves:
18
+ // `e2e` is the deferred half since forge task 3981, which `verify --full`, the queue's suite job and a
19
+ // publish's `--force` run — `verifyGraph.spec.ts` pins which half it sits in.
20
+ const GATE = (pkg as { gate?: { stages?: { script: string }[]; deferred?: { script: string }[] } }).gate;
21
+ const STAGES: readonly { script: string }[] = [...(GATE?.stages ?? []), ...(GATE?.deferred ?? [])];
19
22
 
20
23
  /**
21
24
  * 🔴 The e2e suite must never be able to sit green while it drives nothing.
@@ -159,7 +162,7 @@ describe('the e2e fixture exists', () => {
159
162
  // is still in it, which the `toInclude` never checked for any of them.
160
163
  expect(
161
164
  STAGES.map((s) => s.script),
162
- '`verify` must still run the e2e suite',
165
+ 'the gate (`verify --full`) must still run the e2e suite',
163
166
  ).toContain('e2e');
164
167
  expect(pkg.scripts.e2e).toInclude('e2e/playwright.config.ts');
165
168
  });
@@ -1,4 +1,4 @@
1
- import { type ReactNode, useMemo } from 'react';
1
+ import { type ReactNode, useId, useMemo } from 'react';
2
2
  import { Group, Panel, useDefaultLayout } from 'react-resizable-panels';
3
3
  import { useMediaQuery } from '../hooks/useMediaQuery.js';
4
4
  import { cn } from '../lib/cn.js';
@@ -29,7 +29,10 @@ export interface PanelGridLeaf {
29
29
  /** A split — lays its children out along `direction` with dividers between them. */
30
30
  export interface PanelGridSplit {
31
31
  type: 'split';
32
- /** Unique within the whole grid — namespaces the persisted layout of this group. */
32
+ /**
33
+ * Unique within the whole grid — namespaces the persisted layout of this group. It need not be
34
+ * unique on the PAGE: the library's group is registered as `<this grid's useId()>:<id>`.
35
+ */
33
36
  id: string;
34
37
  /** 'horizontal' = children side-by-side; 'vertical' = children stacked. */
35
38
  direction: 'horizontal' | 'vertical';
@@ -133,6 +136,14 @@ export function PanelGrid({
133
136
  }: PanelGridProps) {
134
137
  const isMobile = useMediaQuery('(max-width: 639px)');
135
138
  const flatLeaves = useMemo(() => leavesOf(root), [root]);
139
+ // 🔴 react-resizable-panels keys its group registry by `id` ACROSS THE DOCUMENT, and each Group's
140
+ // listener takes every change made under its id. `SplitPane` gave every instance the same root
141
+ // id, `'root'`, so two on one page heard each other's layouts: a collapsible panel looked itself
142
+ // up in the other group's layout, found nothing, and `toFixed` threw out of a ResizeObserver —
143
+ // before the listener applied the new layout (forge task 3981, caught by the kitchen sink's
144
+ // `test:browser:smoke`). It also put a second `id="root"` in the DOM beside the app's mount node.
145
+ // Each grid instance scopes its groups; the persisted key stays `${storageKey}:${split.id}`.
146
+ const scope = useId();
136
147
 
137
148
  if (mobileStack && isMobile) {
138
149
  return (
@@ -150,6 +161,7 @@ export function PanelGrid({
150
161
  <GridSplit
151
162
  {...rest}
152
163
  node={root}
164
+ scope={scope}
153
165
  storageKey={storageKey}
154
166
  disabled={disabled}
155
167
  withHandles={withHandles}
@@ -161,6 +173,8 @@ export function PanelGrid({
161
173
 
162
174
  interface GridSplitProps {
163
175
  node: PanelGridSplit;
176
+ /** This grid instance's `useId()` — prefixes every Group id so two grids never share one. */
177
+ scope: string;
164
178
  storageKey: string | undefined;
165
179
  disabled: boolean | undefined;
166
180
  withHandles: boolean;
@@ -176,6 +190,7 @@ function GridSplit(props: GridSplitProps) {
176
190
 
177
191
  function PlainGridSplit({
178
192
  node,
193
+ scope,
179
194
  storageKey,
180
195
  disabled,
181
196
  withHandles,
@@ -184,7 +199,7 @@ function PlainGridSplit({
184
199
  }: GridSplitProps) {
185
200
  return (
186
201
  <Group
187
- id={node.id}
202
+ id={`${scope}:${node.id}`}
188
203
  orientation={node.direction}
189
204
  disabled={disabled}
190
205
  className={cn(
@@ -195,6 +210,7 @@ function PlainGridSplit({
195
210
  >
196
211
  <GridChildren
197
212
  node={node}
213
+ scope={scope}
198
214
  storageKey={storageKey}
199
215
  disabled={disabled}
200
216
  withHandles={withHandles}
@@ -206,6 +222,7 @@ function PlainGridSplit({
206
222
 
207
223
  function PersistentGridSplit({
208
224
  node,
225
+ scope,
209
226
  storageKey,
210
227
  disabled,
211
228
  withHandles,
@@ -219,7 +236,7 @@ function PersistentGridSplit({
219
236
  });
220
237
  return (
221
238
  <Group
222
- id={node.id}
239
+ id={`${scope}:${node.id}`}
223
240
  orientation={node.direction}
224
241
  defaultLayout={defaultLayout}
225
242
  onLayoutChanged={onLayoutChanged}
@@ -232,6 +249,7 @@ function PersistentGridSplit({
232
249
  >
233
250
  <GridChildren
234
251
  node={node}
252
+ scope={scope}
235
253
  storageKey={storageKey}
236
254
  disabled={disabled}
237
255
  withHandles={withHandles}
@@ -243,6 +261,7 @@ function PersistentGridSplit({
243
261
 
244
262
  function GridChildren({
245
263
  node,
264
+ scope,
246
265
  storageKey,
247
266
  disabled,
248
267
  withHandles,
@@ -256,6 +275,7 @@ function GridChildren({
256
275
  <GridCell
257
276
  key={child.id}
258
277
  child={child}
278
+ scope={scope}
259
279
  direction={node.direction}
260
280
  storageKey={storageKey}
261
281
  disabled={disabled}
@@ -270,6 +290,7 @@ function GridChildren({
270
290
 
271
291
  interface GridCellProps {
272
292
  child: PanelGridNode;
293
+ scope: string;
273
294
  direction: 'horizontal' | 'vertical';
274
295
  storageKey: string | undefined;
275
296
  disabled: boolean | undefined;
@@ -280,6 +301,7 @@ interface GridCellProps {
280
301
 
281
302
  function GridCell({
282
303
  child,
304
+ scope,
283
305
  direction,
284
306
  storageKey,
285
307
  disabled,
@@ -308,6 +330,7 @@ function GridCell({
308
330
  {isSplit(child) ? (
309
331
  <GridSplit
310
332
  node={child}
333
+ scope={scope}
311
334
  storageKey={storageKey}
312
335
  disabled={disabled}
313
336
  withHandles={withHandles}
@@ -32,6 +32,23 @@ describe('SplitPane', () => {
32
32
  expect(getByText('Panel C')).toBeTruthy();
33
33
  });
34
34
 
35
+ // 🔴 forge task 3981: every SplitPane registered its group as `'root'`, and react-resizable-panels
36
+ // keys groups by id across the document — two on one page heard each other's layouts and threw
37
+ // `toFixed` of undefined out of a ResizeObserver (the kitchen sink's `test:browser:smoke`), and the
38
+ // page carried a second `id="root"` beside the app's mount node.
39
+ test('two SplitPanes on one page register distinct groups, and neither is id="root"', () => {
40
+ const { container } = render(
41
+ <>
42
+ <SplitPane panels={TWO_PANELS} />
43
+ <SplitPane panels={THREE_PANELS} />
44
+ </>,
45
+ );
46
+ const ids = [...container.querySelectorAll('[data-group]')].map((g) => g.id);
47
+ expect(ids).toHaveLength(2);
48
+ expect(new Set(ids).size).toBe(2);
49
+ expect(container.querySelector('#root')).toBeNull();
50
+ });
51
+
35
52
  test('horizontal layout: Group gets flex-row class', () => {
36
53
  const { container } = render(<SplitPane panels={TWO_PANELS} />);
37
54
  const group = container.querySelector('[data-group]');
@@ -40,8 +40,18 @@ interface Stage {
40
40
  why?: string;
41
41
  }
42
42
 
43
- /** The declared graph, read as data. There is no import of the scheduler to drift from. */
44
- const STAGES: readonly Stage[] = (pkg as { gate?: { stages?: Stage[] } }).gate?.stages ?? [];
43
+ const GATE = (pkg as { gate?: { stages?: Stage[]; deferred?: Stage[] } }).gate;
44
+
45
+ /**
46
+ * The declared graph, read as data — BOTH halves. There is no import of the scheduler to drift from.
47
+ *
48
+ * 🔴 Since 2026-10-29 (forge task 3981) `e2e` sits in `gate.deferred`: plain `verify` runs `stages`
49
+ * and the one `test:browser:smoke` spec, while `verify --full` — the queue's `suite` job after
50
+ * landing — and `--force` run the deferred half too. Moving a stage there is not removing it, so the
51
+ * stage-set floor below reads both halves; what it must never be is in NEITHER.
52
+ */
53
+ const STAGES: readonly Stage[] = [...(GATE?.stages ?? []), ...(GATE?.deferred ?? [])];
54
+ const DEFERRED: readonly string[] = (GATE?.deferred ?? []).map((s) => s.script);
45
55
 
46
56
  /**
47
57
  * What `verify` ran before it became a graph, expanded to the leaf scripts.
@@ -118,6 +128,18 @@ describe('the verify graph', () => {
118
128
  // never let them be replaced. `--force` is the re-roll.
119
129
  expect(pkg.scripts.prepublishOnly).toInclude('--force');
120
130
  });
131
+
132
+ it('🔴 e2e is deferred, never dropped — and a publish still runs it', () => {
133
+ // `$FORGE/tools/gate.ts` → `runnable(spec, full)`: `--force` implies `--full`, which is the
134
+ // deferred half joining the graph. So the `--force` above is also what makes a publish run
135
+ // `e2e`; this pins that the suite is in the half `--force` reaches, and that `verify` keeps
136
+ // the one browser proof check-gate-graph rule 9 asks of a repo deferring its browser suite.
137
+ expect(DEFERRED).toContain('e2e');
138
+ expect(pkg.scripts.gate).not.toInclude('--full');
139
+ const smoke = (GATE?.stages ?? []).find((s) => s.script === 'test:browser:smoke');
140
+ expect(smoke, '`verify` must keep `test:browser:smoke` in `stages`').toBeTruthy();
141
+ expect((pkg.scripts as Record<string, string | undefined>)['test:browser:smoke']).toInclude('smoke.spec.ts');
142
+ });
121
143
  });
122
144
 
123
145
  describe('the two edges the graph rests on', () => {
@@ -126,7 +148,7 @@ describe('the two edges the graph rests on', () => {
126
148
  // stylesStaticMatches.spec.ts and stylesUtilitiesMatches.spec.ts assert those exact files
127
149
  // are current, and the demo bundle e2e drives is compiled from them. Any of those three
128
150
  // running beside the generators is a writer racing a reader.
129
- for (const script of ['build', 'test', 'e2e']) {
151
+ for (const script of ['build', 'test', 'e2e', 'test:browser:smoke']) {
130
152
  const stage = STAGES.find((s) => s.script === script);
131
153
  expect(stage?.needs, `\`${script}\` must wait for \`styles\``).toContain('styles');
132
154
  // 🔴 And it must say WHY. An edge nobody can explain is an edge nobody can safely