@petrarca/sonnet-playground 0.3.0 → 0.4.1

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.
Files changed (30) hide show
  1. package/README.md +142 -0
  2. package/dist/{PlayCoreComponents-F2OFSQZK.js → PlayCoreComponents-GXFMB24T.js} +2 -2
  3. package/dist/{PlayDataComponents-BA3NA225.js → PlayDataComponents-W44OITT5.js} +2 -2
  4. package/dist/{PlayFormComponents-MZC7CNES.js → PlayFormComponents-4E2A2Y2W.js} +2 -2
  5. package/dist/PlayGraph-ADRTDRA7.js +7 -0
  6. package/dist/{PlayShellApi-3T4FZPEV.js → PlayShellApi-5O6VYR2X.js} +2 -2
  7. package/dist/{chunk-MT4ILA37.js → chunk-32DDRRWU.js} +63 -9
  8. package/dist/chunk-32DDRRWU.js.map +1 -0
  9. package/dist/{chunk-SBKZRIJC.js → chunk-4ORSUS65.js} +49 -6
  10. package/dist/chunk-4ORSUS65.js.map +1 -0
  11. package/dist/{chunk-4S2ITDB3.js → chunk-CQ6PGFRV.js} +21 -2
  12. package/dist/chunk-CQ6PGFRV.js.map +1 -0
  13. package/dist/{chunk-DKLALDHR.js → chunk-O2QMWJZI.js} +53 -3
  14. package/dist/chunk-O2QMWJZI.js.map +1 -0
  15. package/dist/{chunk-QCK2M4KD.js → chunk-YOOWJORD.js} +28 -1
  16. package/dist/chunk-YOOWJORD.js.map +1 -0
  17. package/dist/index.js +5 -5
  18. package/dist/pages/index.js +5 -5
  19. package/package.json +6 -6
  20. package/dist/PlayGraph-RHFIDAS3.js +0 -7
  21. package/dist/chunk-4S2ITDB3.js.map +0 -1
  22. package/dist/chunk-DKLALDHR.js.map +0 -1
  23. package/dist/chunk-MT4ILA37.js.map +0 -1
  24. package/dist/chunk-QCK2M4KD.js.map +0 -1
  25. package/dist/chunk-SBKZRIJC.js.map +0 -1
  26. /package/dist/{PlayCoreComponents-F2OFSQZK.js.map → PlayCoreComponents-GXFMB24T.js.map} +0 -0
  27. /package/dist/{PlayDataComponents-BA3NA225.js.map → PlayDataComponents-W44OITT5.js.map} +0 -0
  28. /package/dist/{PlayFormComponents-MZC7CNES.js.map → PlayFormComponents-4E2A2Y2W.js.map} +0 -0
  29. /package/dist/{PlayGraph-RHFIDAS3.js.map → PlayGraph-ADRTDRA7.js.map} +0 -0
  30. /package/dist/{PlayShellApi-3T4FZPEV.js.map → PlayShellApi-5O6VYR2X.js.map} +0 -0
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # @petrarca/sonnet-playground
2
+
3
+ Playground framework and built-in component demos for the Petrarca Sonnet
4
+ component library.
5
+
6
+ ## What's included
7
+
8
+ **Playground framework** — Registry, overview tile grid, and `createPlaygroundModule()`
9
+ factory. Wire a playground into any `sonnet-shell` app with a few lines.
10
+
11
+ **Built-in demos** — Pre-built showcase pages for all Sonnet components:
12
+ Typography, Color Palette, Core UI, Advanced Components, JSON Editor, Forms,
13
+ Form Components, Form Widgets, Data Components, Graph Visualization, Shell API.
14
+
15
+ **PlaygroundSection** — Shared section wrapper (`default` and `card` variants)
16
+ used by all playground pages.
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pnpm add @petrarca/sonnet-playground
24
+ ```
25
+
26
+ Peer dependencies: `react`, `react-dom`, `@petrarca/sonnet-shell`,
27
+ `@petrarca/sonnet-ui`, `@petrarca/sonnet-forms`, `@petrarca/sonnet-graph`.
28
+
29
+ ---
30
+
31
+ ## Basic usage
32
+
33
+ ```ts
34
+ // src/modules/playground/index.ts
35
+ import {
36
+ createPlaygroundRegistry,
37
+ createPlaygroundModule,
38
+ } from "@petrarca/sonnet-playground";
39
+
40
+ const registry = createPlaygroundRegistry();
41
+
42
+ // Optionally add app-specific pages:
43
+ registry.register({
44
+ id: "app.my-page",
45
+ title: "My Page",
46
+ description: "Custom demo page.",
47
+ icon: SomeIcon,
48
+ color: "teal",
49
+ path: "my-page",
50
+ component: React.lazy(() => import("./pages/MyPage")),
51
+ group: "App",
52
+ order: 0,
53
+ });
54
+
55
+ export default createPlaygroundModule(registry);
56
+ ```
57
+
58
+ ```ts
59
+ // src/modules/registry.ts
60
+ import { createModuleRegistry } from "@petrarca/sonnet-shell";
61
+ import home from "./home";
62
+ import playground from "./playground";
63
+
64
+ export const registry = createModuleRegistry([
65
+ home,
66
+ playground, // pinBottom: true is set automatically by createPlaygroundModule
67
+ ]);
68
+ ```
69
+
70
+ `createPlaygroundModule` returns a `ShellModule` — pass it directly to
71
+ `createModuleRegistry` alongside your app's own modules.
72
+
73
+ ---
74
+
75
+ ## Adding app-specific pages
76
+
77
+ Register your own pages alongside the built-in demos by calling
78
+ `registry.register()` before passing the registry to `createPlaygroundModule`.
79
+ Pages appear in the overview tile grid grouped under your chosen `group` label.
80
+
81
+ ```ts
82
+ // src/modules/playground/index.ts
83
+ import { lazy } from "react";
84
+ import { Search } from "lucide-react";
85
+ import {
86
+ createPlaygroundRegistry,
87
+ createPlaygroundModule,
88
+ } from "@petrarca/sonnet-playground";
89
+
90
+ const registry = createPlaygroundRegistry();
91
+
92
+ // Register app-specific pages
93
+ registry.register({
94
+ id: "app.my-widget",
95
+ title: "My Widget",
96
+ description: "Interactive demo of MyWidget with live configuration.",
97
+ icon: Search,
98
+ color: "teal",
99
+ path: "my-widget",
100
+ component: lazy(() => import("./pages/PlayMyWidget")),
101
+ group: "App Components", // appears as a group heading in the overview
102
+ order: 0,
103
+ });
104
+
105
+ export default createPlaygroundModule(registry);
106
+ ```
107
+
108
+ Pages are lazy-loaded automatically — wrap the component in `React.lazy()`
109
+ as shown above. The framework handles the `Suspense` boundary.
110
+
111
+ `group` is optional. Pages without a group appear under the default group in
112
+ the overview. Pages with the same `group` string are grouped together.
113
+
114
+ ---
115
+
116
+ ## Pages subpath
117
+
118
+ Individual built-in pages are available at `@petrarca/sonnet-playground/pages`
119
+ for consumers who want to cherry-pick or re-export them:
120
+
121
+ ```tsx
122
+ import { PlayTypography, PlayColorPalette } from "@petrarca/sonnet-playground/pages";
123
+ ```
124
+
125
+ ---
126
+
127
+ ## createPlaygroundModule options
128
+
129
+ | Option | Type | Default |
130
+ |---|---|---|
131
+ | `icon` | `LucideIcon` | `FlaskConical` |
132
+ | `label` | `string` | `"Playground"` |
133
+ | `basePath` | `string` | `"/playground"` |
134
+ | `overviewTitle` | `string` | `"Developer Playground"` |
135
+ | `overviewSubtitle` | `string` | `""` |
136
+ | `grouped` | `boolean` | `true` |
137
+
138
+ ---
139
+
140
+ ## License
141
+
142
+ See [LICENSE.md](../../LICENSE.md).
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  PlayCoreComponents
3
- } from "./chunk-QCK2M4KD.js";
3
+ } from "./chunk-YOOWJORD.js";
4
4
  import "./chunk-C2EZ2ECG.js";
5
5
  export {
6
6
  PlayCoreComponents as default
7
7
  };
8
- //# sourceMappingURL=PlayCoreComponents-F2OFSQZK.js.map
8
+ //# sourceMappingURL=PlayCoreComponents-GXFMB24T.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  PlayDataComponents
3
- } from "./chunk-DKLALDHR.js";
3
+ } from "./chunk-O2QMWJZI.js";
4
4
  import "./chunk-C2EZ2ECG.js";
5
5
  export {
6
6
  PlayDataComponents as default
7
7
  };
8
- //# sourceMappingURL=PlayDataComponents-BA3NA225.js.map
8
+ //# sourceMappingURL=PlayDataComponents-W44OITT5.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  PlayFormComponents
3
- } from "./chunk-MT4ILA37.js";
3
+ } from "./chunk-32DDRRWU.js";
4
4
  import "./chunk-C2EZ2ECG.js";
5
5
  export {
6
6
  PlayFormComponents as default
7
7
  };
8
- //# sourceMappingURL=PlayFormComponents-MZC7CNES.js.map
8
+ //# sourceMappingURL=PlayFormComponents-4E2A2Y2W.js.map
@@ -0,0 +1,7 @@
1
+ import {
2
+ PlayGraph
3
+ } from "./chunk-4ORSUS65.js";
4
+ export {
5
+ PlayGraph as default
6
+ };
7
+ //# sourceMappingURL=PlayGraph-ADRTDRA7.js.map
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  PlayShellApi
3
- } from "./chunk-4S2ITDB3.js";
3
+ } from "./chunk-CQ6PGFRV.js";
4
4
  import "./chunk-C2EZ2ECG.js";
5
5
  export {
6
6
  PlayShellApi as default
7
7
  };
8
- //# sourceMappingURL=PlayShellApi-3T4FZPEV.js.map
8
+ //# sourceMappingURL=PlayShellApi-5O6VYR2X.js.map
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-C2EZ2ECG.js";
4
4
 
5
5
  // src/pages/PlayFormComponents.tsx
6
- import { useState } from "react";
6
+ import { useState, useCallback, useRef } from "react";
7
7
  import { SimpleStack, Card } from "@petrarca/sonnet-ui";
8
8
  import {
9
9
  FormInput,
@@ -18,8 +18,16 @@ import { jsx, jsxs } from "react/jsx-runtime";
18
18
  function PlayFormComponents() {
19
19
  const [textValue, setTextValue] = useState("");
20
20
  const [emailValue, setEmailValue] = useState("");
21
+ const [emailError, setEmailError] = useState("");
21
22
  const [passwordValue, setPasswordValue] = useState("");
22
- const [urlValue, setUrlValue] = useState("");
23
+ const validateEmail = useCallback((value) => {
24
+ if (!value) {
25
+ setEmailError("");
26
+ return;
27
+ }
28
+ const valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
29
+ setEmailError(valid ? "" : "Invalid email address");
30
+ }, []);
23
31
  const [textareaValue, setTextareaValue] = useState("");
24
32
  const [textareaError, setTextareaError] = useState("");
25
33
  const [numberValue, setNumberValue] = useState(void 0);
@@ -27,6 +35,19 @@ function PlayFormComponents() {
27
35
  const [decimalValue, setDecimalValue] = useState(
28
36
  void 0
29
37
  );
38
+ const [rangeValidated, setRangeValidated] = useState(
39
+ void 0
40
+ );
41
+ const [rangeError, setRangeError] = useState("");
42
+ const rangeValidatedRef = useRef(void 0);
43
+ const validateRange = useCallback((value) => {
44
+ if (value === void 0) {
45
+ setRangeError("");
46
+ return;
47
+ }
48
+ if (value < 1 || value > 10) setRangeError("Must be between 1 and 10");
49
+ else setRangeError("");
50
+ }, []);
30
51
  const [checkboxValue, setCheckboxValue] = useState(false);
31
52
  const [checkboxIndeterminate, setCheckboxIndeterminate] = useState("indeterminate");
32
53
  const [selectValue, setSelectValue] = useState("");
@@ -88,7 +109,7 @@ function PlayFormComponents() {
88
109
  placeholder: "john@example.com"
89
110
  }
90
111
  ) }),
91
- /* @__PURE__ */ jsx(ExampleBlock, { label: "With error", children: /* @__PURE__ */ jsx(
112
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "WITH ERROR (STATIC)", children: /* @__PURE__ */ jsx(
92
113
  FormInput,
93
114
  {
94
115
  label: "Email",
@@ -98,6 +119,21 @@ function PlayFormComponents() {
98
119
  placeholder: "john@example.com"
99
120
  }
100
121
  ) }),
122
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "Email (on-blur validation)", children: /* @__PURE__ */ jsx(
123
+ FormInput,
124
+ {
125
+ label: "Email",
126
+ type: "email",
127
+ value: emailValue,
128
+ onChange: (e) => {
129
+ setEmailValue(e.target.value);
130
+ if (emailError) validateEmail(e.target.value);
131
+ },
132
+ onBlur: (e) => validateEmail(e.target.value),
133
+ error: emailError,
134
+ placeholder: "user@example.com"
135
+ }
136
+ ) }),
101
137
  /* @__PURE__ */ jsx(ExampleBlock, { label: "Password", children: /* @__PURE__ */ jsx(
102
138
  FormInput,
103
139
  {
@@ -249,7 +285,7 @@ function PlayFormComponents() {
249
285
  placeholder: "0"
250
286
  }
251
287
  ) }),
252
- /* @__PURE__ */ jsx(ExampleBlock, { label: "With error", children: /* @__PURE__ */ jsx(
288
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "WITH ERROR (STATIC)", children: /* @__PURE__ */ jsx(
253
289
  FormNumberInput,
254
290
  {
255
291
  label: "Invalid Input",
@@ -257,6 +293,24 @@ function PlayFormComponents() {
257
293
  onChange: setNumberValue,
258
294
  error: "Value must be greater than 0"
259
295
  }
296
+ ) }),
297
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "Range (on-blur validation)", children: /* @__PURE__ */ jsx(
298
+ FormNumberInput,
299
+ {
300
+ label: "Rating (1\u201310)",
301
+ value: rangeValidated,
302
+ onChange: (v) => {
303
+ setRangeValidated(v);
304
+ rangeValidatedRef.current = v;
305
+ if (rangeError) validateRange(v);
306
+ },
307
+ onBlur: () => validateRange(rangeValidatedRef.current),
308
+ error: rangeError,
309
+ min: 1,
310
+ max: 10,
311
+ step: 1,
312
+ placeholder: "1\u201310"
313
+ }
260
314
  ) })
261
315
  ] }),
262
316
  /* @__PURE__ */ jsx("pre", { className: "text-xs bg-muted p-2 rounded overflow-x-auto", children: /* @__PURE__ */ jsx("code", { children: `<FormNumberInput
@@ -322,7 +376,7 @@ function PlayFormComponents() {
322
376
  disabled: true
323
377
  }
324
378
  ) }),
325
- /* @__PURE__ */ jsx(ExampleBlock, { label: "With error", children: /* @__PURE__ */ jsx(
379
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "WITH ERROR (STATIC)", children: /* @__PURE__ */ jsx(
326
380
  FormCheckbox,
327
381
  {
328
382
  label: "Required checkbox",
@@ -392,7 +446,7 @@ function PlayFormComponents() {
392
446
  placeholder: "Cannot change"
393
447
  }
394
448
  ) }),
395
- /* @__PURE__ */ jsx(ExampleBlock, { label: "With error", children: /* @__PURE__ */ jsx(
449
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "WITH ERROR (STATIC)", children: /* @__PURE__ */ jsx(
396
450
  FormSelect,
397
451
  {
398
452
  label: "Invalid Selection",
@@ -460,7 +514,7 @@ function PlayFormComponents() {
460
514
  placeholder: "Select at least one skill"
461
515
  }
462
516
  ) }),
463
- /* @__PURE__ */ jsx(ExampleBlock, { label: "With error", children: /* @__PURE__ */ jsx(
517
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "WITH ERROR (STATIC)", children: /* @__PURE__ */ jsx(
464
518
  FormMultiSelect,
465
519
  {
466
520
  label: "Categories",
@@ -518,7 +572,7 @@ function PlayFormComponents() {
518
572
  placeholder: "Add at least one keyword"
519
573
  }
520
574
  ) }),
521
- /* @__PURE__ */ jsx(ExampleBlock, { label: "With error", children: /* @__PURE__ */ jsx(
575
+ /* @__PURE__ */ jsx(ExampleBlock, { label: "WITH ERROR (STATIC)", children: /* @__PURE__ */ jsx(
522
576
  FormTagsInput,
523
577
  {
524
578
  label: "Tags",
@@ -560,4 +614,4 @@ function PlayFormComponents() {
560
614
  export {
561
615
  PlayFormComponents
562
616
  };
563
- //# sourceMappingURL=chunk-MT4ILA37.js.map
617
+ //# sourceMappingURL=chunk-32DDRRWU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pages/PlayFormComponents.tsx"],"sourcesContent":["/**\n * Playground page for testing Form components\n * Accessible at /playground/form-components\n *\n * Tests all custom Form wrapper components that provide consistent\n * label, description, and error handling around base UI components.\n */\n\nimport { useState, useCallback, useRef } from \"react\";\nimport { SimpleStack, Card } from \"@petrarca/sonnet-ui\";\nimport { ExampleBlock } from \"../PlaygroundSection\";\nimport {\n FormInput,\n FormTextarea,\n FormNumberInput,\n FormCheckbox,\n FormSelect,\n FormMultiSelect,\n FormTagsInput,\n} from \"@petrarca/sonnet-forms\";\n\nexport default function PlayFormComponents() {\n // FormInput states\n const [textValue, setTextValue] = useState(\"\");\n const [emailValue, setEmailValue] = useState(\"\");\n const [emailError, setEmailError] = useState(\"\");\n const [passwordValue, setPasswordValue] = useState(\"\");\n\n const validateEmail = useCallback((value: string) => {\n if (!value) {\n setEmailError(\"\");\n return;\n }\n const valid = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value);\n setEmailError(valid ? \"\" : \"Invalid email address\");\n }, []);\n\n // FormTextarea states\n const [textareaValue, setTextareaValue] = useState(\"\");\n const [textareaError, setTextareaError] = useState(\"\");\n\n // FormNumberInput states\n const [numberValue, setNumberValue] = useState<number | undefined>(undefined);\n const [rangeValue, setRangeValue] = useState<number | undefined>(5);\n const [decimalValue, setDecimalValue] = useState<number | undefined>(\n undefined,\n );\n const [rangeValidated, setRangeValidated] = useState<number | undefined>(\n undefined,\n );\n const [rangeError, setRangeError] = useState(\"\");\n const rangeValidatedRef = useRef<number | undefined>(undefined);\n\n const validateRange = useCallback((value: number | undefined) => {\n if (value === undefined) {\n setRangeError(\"\");\n return;\n }\n if (value < 1 || value > 10) setRangeError(\"Must be between 1 and 10\");\n else setRangeError(\"\");\n }, []);\n\n // FormCheckbox states\n const [checkboxValue, setCheckboxValue] = useState(false);\n const [checkboxIndeterminate, setCheckboxIndeterminate] = useState<\n boolean | \"indeterminate\"\n >(\"indeterminate\");\n\n // FormSelect states\n const [selectValue, setSelectValue] = useState(\"\");\n const [selectRequired, setSelectRequired] = useState(\"\");\n\n // FormMultiSelect states\n const [multiSelectValue, setMultiSelectValue] = useState<string[]>([]);\n\n // FormTagsInput states\n const [tagsValue, setTagsValue] = useState<string[]>([\"react\", \"typescript\"]);\n\n // Options for selects\n const colorOptions = [\n { label: \"Red\", value: \"red\" },\n { label: \"Green\", value: \"green\" },\n { label: \"Blue\", value: \"blue\" },\n { label: \"Yellow\", value: \"yellow\" },\n { label: \"Purple\", value: \"purple\" },\n ];\n\n const countryOptions = [\n { label: \"United States\", value: \"us\" },\n { label: \"Canada\", value: \"ca\" },\n { label: \"United Kingdom\", value: \"uk\" },\n { label: \"Germany\", value: \"de\" },\n { label: \"France\", value: \"fr\" },\n { label: \"Japan\", value: \"jp\" },\n { label: \"Australia\", value: \"au\" },\n ];\n\n return (\n <div className=\"w-full max-w-4xl space-y-6\">\n <div>\n <h1>Form Components Playground</h1>\n <p className=\"text-sm text-muted-foreground mb-3\">\n Test all custom Form wrapper components with labels, descriptions, and\n error states\n </p>\n </div>\n\n <div>\n <SimpleStack gap=\"md\" style={{ maxWidth: \"900px\" }}>\n {/* FormInput */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormInput</h2>\n <p className=\"text-sm text-muted-foreground\">\n Text input wrapper with label, description, and error support.\n Supports all HTML input types.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormInput\n label=\"Username\"\n value={textValue}\n onChange={(e) => setTextValue(e.target.value)}\n placeholder=\"Enter username\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"With description\">\n <FormInput\n label=\"Full Name\"\n description=\"Your first and last name\"\n value={textValue}\n onChange={(e) => setTextValue(e.target.value)}\n placeholder=\"John Doe\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Required\">\n <FormInput\n label=\"Email\"\n required\n value={emailValue}\n onChange={(e) => setEmailValue(e.target.value)}\n placeholder=\"john@example.com\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"WITH ERROR (STATIC)\">\n <FormInput\n label=\"Email\"\n value={emailValue}\n onChange={(e) => setEmailValue(e.target.value)}\n error=\"Invalid email format\"\n placeholder=\"john@example.com\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Email (on-blur validation)\">\n <FormInput\n label=\"Email\"\n type=\"email\"\n value={emailValue}\n onChange={(e) => {\n setEmailValue(e.target.value);\n if (emailError) validateEmail(e.target.value);\n }}\n onBlur={(e) => validateEmail(e.target.value)}\n error={emailError}\n placeholder=\"user@example.com\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Password\">\n <FormInput\n label=\"Password\"\n type=\"password\"\n value={passwordValue}\n onChange={(e) => setPasswordValue(e.target.value)}\n placeholder=\"••••••••\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Disabled\">\n <FormInput\n label=\"Readonly Field\"\n value=\"Cannot edit this\"\n onChange={() => {}}\n disabled\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormInput\n label=\"Username\"\n value={textValue}\n onChange={(e) => setTextValue(e.target.value)}\n placeholder=\"Enter username\"\n description=\"Optional description\"\n error=\"Optional error message\"\n required={false}\n disabled={false}\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current value: {textValue || \"(empty)\"}\n </p>\n </SimpleStack>\n </Card>\n\n {/* FormTextarea */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormTextarea</h2>\n <p className=\"text-sm text-muted-foreground\">\n Multi-line text input with label, description, and error\n support.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormTextarea\n label=\"Description\"\n value={textareaValue}\n onChange={(e) => setTextareaValue(e.target.value)}\n placeholder=\"Enter a detailed description...\"\n rows={4}\n />\n </ExampleBlock>\n <ExampleBlock label=\"With description\">\n <FormTextarea\n label=\"Bio\"\n description=\"Tell us about yourself (max 500 characters)\"\n value={textareaValue}\n onChange={(e) => setTextareaValue(e.target.value)}\n placeholder=\"I am a...\"\n rows={3}\n />\n </ExampleBlock>\n <ExampleBlock label=\"With error validation\">\n <FormTextarea\n label=\"Comments\"\n value={textareaValue}\n onChange={(e) => {\n setTextareaValue(e.target.value);\n setTextareaError(\n e.target.value.length > 100\n ? \"Maximum 100 characters allowed\"\n : \"\",\n );\n }}\n error={textareaError}\n placeholder=\"Enter your comments...\"\n rows={3}\n />\n </ExampleBlock>\n <ExampleBlock label=\"Disabled\">\n <FormTextarea\n label=\"Readonly Text\"\n value=\"This cannot be edited\"\n onChange={() => {}}\n disabled\n rows={2}\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormTextarea\n label=\"Description\"\n value={textareaValue}\n onChange={(e) => setTextareaValue(e.target.value)}\n placeholder=\"Enter text...\"\n rows={4}\n description=\"Optional description\"\n error=\"Optional error message\"\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current value: {textareaValue || \"(empty)\"}\n </p>\n </SimpleStack>\n </Card>\n\n {/* FormNumberInput */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormNumberInput</h2>\n <p className=\"text-sm text-muted-foreground\">\n Number input with min/max validation and step control.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormNumberInput\n label=\"Age\"\n value={numberValue}\n onChange={setNumberValue}\n placeholder=\"Enter age\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"With min/max/step\">\n <FormNumberInput\n label=\"Rating (1-10)\"\n value={rangeValue}\n onChange={setRangeValue}\n min={1}\n max={10}\n step={1}\n description=\"Rate from 1 to 10\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Decimal\">\n <FormNumberInput\n label=\"Price\"\n value={decimalValue}\n onChange={setDecimalValue}\n allowDecimal={true}\n step={0.01}\n placeholder=\"0.00\"\n description=\"Enter price with cents\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Required\">\n <FormNumberInput\n label=\"Quantity\"\n required\n value={numberValue}\n onChange={setNumberValue}\n min={0}\n placeholder=\"0\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"WITH ERROR (STATIC)\">\n <FormNumberInput\n label=\"Invalid Input\"\n value={numberValue}\n onChange={setNumberValue}\n error=\"Value must be greater than 0\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Range (on-blur validation)\">\n <FormNumberInput\n label=\"Rating (1–10)\"\n value={rangeValidated}\n onChange={(v) => {\n setRangeValidated(v);\n rangeValidatedRef.current = v;\n if (rangeError) validateRange(v);\n }}\n onBlur={() => validateRange(rangeValidatedRef.current)}\n error={rangeError}\n min={1}\n max={10}\n step={1}\n placeholder=\"1–10\"\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormNumberInput\n label=\"Age\"\n value={numberValue}\n onChange={setNumberValue}\n min={0}\n max={120}\n step={1}\n allowDecimal={false}\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current value: {numberValue ?? \"(empty)\"}\n </p>\n </SimpleStack>\n </Card>\n\n {/* FormCheckbox */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormCheckbox</h2>\n <p className=\"text-sm text-muted-foreground\">\n Checkbox with label, description, and indeterminate state\n support.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormCheckbox\n label=\"Accept terms and conditions\"\n checked={checkboxValue}\n onChange={(checked) => setCheckboxValue(checked === true)}\n />\n </ExampleBlock>\n <ExampleBlock label=\"With description\">\n <FormCheckbox\n label=\"Subscribe to newsletter\"\n description=\"Receive weekly updates about new features\"\n checked={checkboxValue}\n onChange={(checked) => setCheckboxValue(checked === true)}\n />\n </ExampleBlock>\n <ExampleBlock label=\"Required\">\n <FormCheckbox\n label=\"I agree to the privacy policy\"\n required\n checked={checkboxValue}\n onChange={(checked) => setCheckboxValue(checked === true)}\n />\n </ExampleBlock>\n <ExampleBlock label=\"Indeterminate\">\n <FormCheckbox\n label=\"Select all items\"\n description=\"Some items are selected\"\n checked={checkboxIndeterminate}\n onChange={setCheckboxIndeterminate}\n />\n </ExampleBlock>\n <ExampleBlock label=\"Disabled\">\n <FormCheckbox\n label=\"Cannot change this\"\n checked={true}\n onChange={() => {}}\n disabled\n />\n </ExampleBlock>\n <ExampleBlock label=\"WITH ERROR (STATIC)\">\n <FormCheckbox\n label=\"Required checkbox\"\n checked={checkboxValue}\n onChange={(checked) => setCheckboxValue(checked === true)}\n error=\"You must accept to continue\"\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormCheckbox\n label=\"Accept terms\"\n checked={checkboxValue}\n onChange={(checked) => setCheckboxValue(checked === true)}\n description=\"Optional description\"\n required={false}\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current value: {String(checkboxValue)}\n </p>\n </SimpleStack>\n </Card>\n\n {/* FormSelect */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormSelect</h2>\n <p className=\"text-sm text-muted-foreground\">\n Searchable single-select dropdown with label and error support.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormSelect\n label=\"Favorite Color\"\n value={selectValue}\n onChange={(value) => setSelectValue(value || \"\")}\n options={colorOptions}\n placeholder=\"Choose a color\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"With description\">\n <FormSelect\n label=\"Country\"\n description=\"Select your country of residence\"\n value={selectValue}\n onChange={(value) => setSelectValue(value || \"\")}\n options={countryOptions}\n placeholder=\"Select country\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Required\">\n <FormSelect\n label=\"Required Selection\"\n required\n value={selectRequired}\n onChange={(value) => setSelectRequired(value || \"\")}\n options={colorOptions}\n placeholder=\"Must select one\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Disabled\">\n <FormSelect\n label=\"Disabled Select\"\n value=\"blue\"\n onChange={() => {}}\n options={colorOptions}\n disabled\n placeholder=\"Cannot change\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"WITH ERROR (STATIC)\">\n <FormSelect\n label=\"Invalid Selection\"\n value={selectValue}\n onChange={(value) => setSelectValue(value || \"\")}\n options={colorOptions}\n error=\"This field is required\"\n placeholder=\"Select option\"\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormSelect\n label=\"Country\"\n value={selectValue}\n onChange={(value) => setSelectValue(value || '')}\n options={[\n { label: 'USA', value: 'us' },\n { label: 'UK', value: 'uk' }\n ]}\n placeholder=\"Select...\"\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current value: {selectValue || \"(none selected)\"}\n </p>\n </SimpleStack>\n </Card>\n\n {/* FormMultiSelect */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormMultiSelect</h2>\n <p className=\"text-sm text-muted-foreground\">\n Searchable multi-select dropdown with badges.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormMultiSelect\n label=\"Favorite Colors\"\n value={multiSelectValue}\n onChange={setMultiSelectValue}\n options={colorOptions}\n placeholder=\"Select multiple colors\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"With description\">\n <FormMultiSelect\n label=\"Visited Countries\"\n description=\"Select all countries you've visited\"\n value={multiSelectValue}\n onChange={setMultiSelectValue}\n options={countryOptions}\n placeholder=\"Select countries\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Required\">\n <FormMultiSelect\n label=\"Skills\"\n required\n value={multiSelectValue}\n onChange={setMultiSelectValue}\n options={[\n { label: \"JavaScript\", value: \"js\" },\n { label: \"TypeScript\", value: \"ts\" },\n { label: \"Python\", value: \"py\" },\n { label: \"Java\", value: \"java\" },\n ]}\n placeholder=\"Select at least one skill\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"WITH ERROR (STATIC)\">\n <FormMultiSelect\n label=\"Categories\"\n value={multiSelectValue}\n onChange={setMultiSelectValue}\n options={colorOptions}\n error=\"Please select at least 2 categories\"\n placeholder=\"Select categories\"\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormMultiSelect\n label=\"Skills\"\n value={multiSelectValue}\n onChange={setMultiSelectValue}\n options={skillOptions}\n placeholder=\"Select skills\"\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current value:{\" \"}\n {multiSelectValue.length > 0\n ? multiSelectValue.join(\", \")\n : \"(none selected)\"}\n </p>\n </SimpleStack>\n </Card>\n\n {/* FormTagsInput */}\n <Card className=\"border shadow-sm p-4\">\n <SimpleStack gap=\"sm\">\n <h2>FormTagsInput</h2>\n <p className=\"text-sm text-muted-foreground\">\n Free-text tag input with creation and deletion.\n </p>\n\n <div className=\"grid grid-cols-2 gap-3 pt-2\">\n <ExampleBlock label=\"Basic\">\n <FormTagsInput\n label=\"Technologies\"\n value={tagsValue}\n onChange={setTagsValue}\n placeholder=\"Type and press Enter to add tags\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"With description\">\n <FormTagsInput\n label=\"Skills\"\n description=\"Add your programming skills (press Enter after each)\"\n value={tagsValue}\n onChange={setTagsValue}\n placeholder=\"e.g., React, Node.js\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Required\">\n <FormTagsInput\n label=\"Keywords\"\n required\n value={tagsValue}\n onChange={setTagsValue}\n placeholder=\"Add at least one keyword\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"WITH ERROR (STATIC)\">\n <FormTagsInput\n label=\"Tags\"\n value={tagsValue}\n onChange={setTagsValue}\n error=\"Maximum 5 tags allowed\"\n placeholder=\"Add tags\"\n />\n </ExampleBlock>\n <ExampleBlock label=\"Disabled\">\n <FormTagsInput\n label=\"Fixed Tags\"\n value={[\"readonly\", \"tags\"]}\n onChange={() => {}}\n disabled\n placeholder=\"Cannot edit\"\n />\n </ExampleBlock>\n </div>\n\n <pre className=\"text-xs bg-muted p-2 rounded overflow-x-auto\">\n <code>{`<FormTagsInput\n label=\"Technologies\"\n value={tagsValue}\n onChange={setTagsValue}\n placeholder=\"Type and press Enter\"\n description=\"Optional description\"\n/>`}</code>\n </pre>\n\n <p className=\"text-xs text-muted-foreground\">\n Current tags:{\" \"}\n {tagsValue.length > 0 ? tagsValue.join(\", \") : \"(none)\"}\n </p>\n </SimpleStack>\n </Card>\n </SimpleStack>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,8 +1,9 @@
1
1
  // src/pages/PlayGraph.tsx
2
- import { useCallback, useState } from "react";
2
+ import { useCallback, useRef, useState } from "react";
3
3
  import {
4
4
  GraphVisualizer,
5
5
  ActionPanel,
6
+ GraphA11yList,
6
7
  GraphRendererProvider,
7
8
  GraphStoresContext,
8
9
  createGraphDataStore,
@@ -21,7 +22,8 @@ import {
21
22
  Tabs,
22
23
  TabsContent,
23
24
  TabsList,
24
- TabsTrigger
25
+ TabsTrigger,
26
+ ResizeHandle
25
27
  } from "@petrarca/sonnet-ui";
26
28
  import { Play, RotateCcw } from "lucide-react";
27
29
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -174,7 +176,7 @@ function createPlaygroundStores() {
174
176
  graphFocus,
175
177
  graphFilter,
176
178
  // Static data -- no server expansion available in the playground
177
- expandNode: async () => ({ nodes: [], edges: [] }),
179
+ expandNode: async () => ({ nodes: [], edges: [], has_more: false }),
178
180
  getNodes: async () => []
179
181
  });
180
182
  return { graphFilter, graphData, graphExploration, selection };
@@ -238,6 +240,23 @@ function PlayGraph() {
238
240
  () => serializeGraphData(INITIAL_DATA)
239
241
  );
240
242
  const [parseError, setParseError] = useState(null);
243
+ const [panelWidth, setPanelWidth] = useState(300);
244
+ const rowRef = useRef(null);
245
+ const startResize = useCallback((e) => {
246
+ e.preventDefault();
247
+ const target = e.currentTarget;
248
+ target.setPointerCapture(e.pointerId);
249
+ const move = (ev) => {
250
+ const right = rowRef.current?.getBoundingClientRect().right ?? 0;
251
+ setPanelWidth(Math.min(560, Math.max(200, right - ev.clientX)));
252
+ };
253
+ const up = () => {
254
+ window.removeEventListener("pointermove", move);
255
+ window.removeEventListener("pointerup", up);
256
+ };
257
+ window.addEventListener("pointermove", move);
258
+ window.addEventListener("pointerup", up);
259
+ }, []);
241
260
  const handleApply = useCallback(() => {
242
261
  let parsed;
243
262
  try {
@@ -278,9 +297,33 @@ function PlayGraph() {
278
297
  /* @__PURE__ */ jsx(TabsTrigger, { value: "graph", children: "Graph" }),
279
298
  /* @__PURE__ */ jsx(TabsTrigger, { value: "data", children: "Data" })
280
299
  ] }),
281
- /* @__PURE__ */ jsx(TabsContent, { value: "graph", className: "mt-0", children: /* @__PURE__ */ jsx(GraphRendererProvider, { defaultRenderer: "cytoscape", children: /* @__PURE__ */ jsxs("div", { className: "flex border rounded-lg overflow-hidden h-[600px]", children: [
300
+ /* @__PURE__ */ jsx(TabsContent, { value: "graph", className: "mt-0", children: /* @__PURE__ */ jsx(GraphRendererProvider, { defaultRenderer: "cytoscape", children: /* @__PURE__ */ jsxs("div", { ref: rowRef, className: "flex overflow-hidden h-[600px]", children: [
282
301
  /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(GraphVisualizer, {}) }),
283
- /* @__PURE__ */ jsx("div", { className: "w-[300px] flex-shrink-0 border-l", children: /* @__PURE__ */ jsx(ActionPanel, {}) })
302
+ /* @__PURE__ */ jsx(
303
+ ResizeHandle,
304
+ {
305
+ orientation: "vertical",
306
+ "aria-valuenow": panelWidth,
307
+ "aria-valuemin": 200,
308
+ "aria-valuemax": 560,
309
+ onPointerDown: startResize,
310
+ onDoubleClick: () => setPanelWidth(300)
311
+ }
312
+ ),
313
+ /* @__PURE__ */ jsxs(
314
+ "div",
315
+ {
316
+ style: { width: panelWidth },
317
+ className: "flex-shrink-0 flex flex-col min-h-0",
318
+ children: [
319
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx(ActionPanel, {}) }),
320
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 px-4 pb-4 pt-2", children: [
321
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2", children: "Accessible list (demo)" }),
322
+ /* @__PURE__ */ jsx(GraphA11yList, { visible: true, maxHeight: 160 })
323
+ ] })
324
+ ]
325
+ }
326
+ )
284
327
  ] }) }) }),
285
328
  /* @__PURE__ */ jsx(TabsContent, { value: "data", className: "mt-0", children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
286
329
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -313,4 +356,4 @@ function PlayGraph() {
313
356
  export {
314
357
  PlayGraph
315
358
  };
316
- //# sourceMappingURL=chunk-SBKZRIJC.js.map
359
+ //# sourceMappingURL=chunk-4ORSUS65.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/pages/PlayGraph.tsx"],"sourcesContent":["/**\n * Playground page for @petrarca/sonnet-graph.\n * Accessible at /playground/graph\n *\n * Demonstrates graph visualization with static mock data -- no backend required.\n * Uses the GraphStoresContext provider pattern that consumers like explorer\n * and Atlas would use to wire their own stores.\n */\n\nimport React, { useCallback, useRef, useState } from \"react\";\nimport {\n GraphVisualizer,\n ActionPanel,\n GraphA11yList,\n GraphRendererProvider,\n GraphStoresContext,\n createGraphDataStore,\n createGraphFilterStore,\n createGraphExplorationStore,\n createSelectionStore,\n createGraphFocusStore,\n enrichNodes,\n enrichEdges,\n buildColorPalette,\n type RawGraphNode,\n type RawGraphEdge,\n type WorkspaceStores,\n} from \"@petrarca/sonnet-graph\";\nimport {\n Button,\n Alert,\n AlertDescription,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n ResizeHandle,\n} from \"@petrarca/sonnet-ui\";\nimport { Play, RotateCcw } from \"lucide-react\";\n\n// ---------------------------------------------------------------------------\n// Mock data -- a small org hierarchy\n// ---------------------------------------------------------------------------\n\nconst MOCK_NODES: RawGraphNode[] = [\n {\n id_: 1,\n label_: \"Company\",\n properties_: { name: \"Acme Corp\", founded: 1985, industry: \"Technology\" },\n },\n {\n id_: 2,\n label_: \"Department\",\n properties_: { name: \"Engineering\", headcount: 42 },\n },\n {\n id_: 3,\n label_: \"Department\",\n properties_: { name: \"Marketing\", headcount: 18 },\n },\n {\n id_: 4,\n label_: \"Department\",\n properties_: { name: \"Sales\", headcount: 25 },\n },\n {\n id_: 5,\n label_: \"Person\",\n properties_: { name: \"Alice Chen\", role: \"CTO\" },\n },\n {\n id_: 6,\n label_: \"Person\",\n properties_: { name: \"Bob Smith\", role: \"VP Engineering\" },\n },\n {\n id_: 7,\n label_: \"Person\",\n properties_: { name: \"Carol Davis\", role: \"CMO\" },\n },\n {\n id_: 8,\n label_: \"Person\",\n properties_: { name: \"Dave Wilson\", role: \"VP Sales\" },\n },\n {\n id_: 9,\n label_: \"Person\",\n properties_: { name: \"Eve Johnson\", role: \"Senior Engineer\" },\n },\n {\n id_: 10,\n label_: \"Person\",\n properties_: { name: \"Frank Brown\", role: \"Product Manager\" },\n },\n {\n id_: 11,\n label_: \"Product\",\n properties_: { name: \"CloudSuite\", version: \"3.2\", status: \"GA\" },\n },\n {\n id_: 12,\n label_: \"Product\",\n properties_: { name: \"DataPipeline\", version: \"1.0\", status: \"Beta\" },\n },\n];\n\nconst MOCK_EDGES: RawGraphEdge[] = [\n {\n id_: 101,\n label_: \"HAS_DEPARTMENT\",\n start_id_: 1,\n end_id_: 2,\n properties_: {},\n },\n {\n id_: 102,\n label_: \"HAS_DEPARTMENT\",\n start_id_: 1,\n end_id_: 3,\n properties_: {},\n },\n {\n id_: 103,\n label_: \"HAS_DEPARTMENT\",\n start_id_: 1,\n end_id_: 4,\n properties_: {},\n },\n {\n id_: 104,\n label_: \"LEADS\",\n start_id_: 5,\n end_id_: 2,\n properties_: { since: 2019 },\n },\n {\n id_: 105,\n label_: \"WORKS_IN\",\n start_id_: 6,\n end_id_: 2,\n properties_: { since: 2020 },\n },\n {\n id_: 106,\n label_: \"LEADS\",\n start_id_: 7,\n end_id_: 3,\n properties_: { since: 2021 },\n },\n {\n id_: 107,\n label_: \"LEADS\",\n start_id_: 8,\n end_id_: 4,\n properties_: { since: 2018 },\n },\n {\n id_: 108,\n label_: \"WORKS_IN\",\n start_id_: 9,\n end_id_: 2,\n properties_: { since: 2022 },\n },\n {\n id_: 109,\n label_: \"WORKS_IN\",\n start_id_: 10,\n end_id_: 2,\n properties_: { since: 2021 },\n },\n { id_: 110, label_: \"REPORTS_TO\", start_id_: 6, end_id_: 5, properties_: {} },\n { id_: 111, label_: \"REPORTS_TO\", start_id_: 9, end_id_: 6, properties_: {} },\n {\n id_: 112,\n label_: \"REPORTS_TO\",\n start_id_: 10,\n end_id_: 6,\n properties_: {},\n },\n { id_: 113, label_: \"OWNS\", start_id_: 2, end_id_: 11, properties_: {} },\n { id_: 114, label_: \"OWNS\", start_id_: 2, end_id_: 12, properties_: {} },\n];\n\n// ---------------------------------------------------------------------------\n// Store setup\n//\n// Stores are created once at module load, not inside the component.\n// This avoids re-creation on re-render and removes the need for useMemo.\n// ---------------------------------------------------------------------------\n\nfunction createPlaygroundStores(): WorkspaceStores {\n const graphFilter = createGraphFilterStore();\n const graphData = createGraphDataStore();\n const selection = createSelectionStore();\n const graphFocus = createGraphFocusStore();\n const graphExploration = createGraphExplorationStore({\n graphData,\n selection,\n graphFocus,\n graphFilter,\n // Static data -- no server expansion available in the playground\n expandNode: async () => ({ nodes: [], edges: [], has_more: false }),\n getNodes: async () => [],\n });\n return { graphFilter, graphData, graphExploration, selection };\n}\n\nconst PLAYGROUND_STORES = createPlaygroundStores();\n\n// ---------------------------------------------------------------------------\n// Data helpers\n// ---------------------------------------------------------------------------\n\ninterface GraphInput {\n nodes: RawGraphNode[];\n edges: RawGraphEdge[];\n}\n\nconst INITIAL_DATA: GraphInput = { nodes: MOCK_NODES, edges: MOCK_EDGES };\n\nfunction serializeGraphData(data: GraphInput): string {\n return JSON.stringify(data, null, 2);\n}\n\nfunction validateNode(n: unknown, i: number): string | null {\n if (!n || typeof n !== \"object\") return `nodes[${i}] must be an object`;\n const node = n as Record<string, unknown>;\n if (node.id_ === undefined) return `nodes[${i}] is missing id_`;\n if (typeof node.label_ !== \"string\") return `nodes[${i}] is missing label_`;\n return null;\n}\n\nfunction validateEdge(e: unknown, i: number): string | null {\n if (!e || typeof e !== \"object\") return `edges[${i}] must be an object`;\n const edge = e as Record<string, unknown>;\n if (edge.id_ === undefined) return `edges[${i}] is missing id_`;\n if (typeof edge.label_ !== \"string\") return `edges[${i}] is missing label_`;\n if (edge.start_id_ === undefined) return `edges[${i}] is missing start_id_`;\n if (edge.end_id_ === undefined) return `edges[${i}] is missing end_id_`;\n return null;\n}\n\n/** Validates that parsed JSON is a usable GraphInput. Returns an error string or null. */\nfunction validateGraphInput(value: unknown): string | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return \"JSON must be an object with nodes and edges arrays\";\n }\n const obj = value as Record<string, unknown>;\n if (!Array.isArray(obj.nodes)) return '\"nodes\" must be an array';\n if (!Array.isArray(obj.edges)) return '\"edges\" must be an array';\n for (const [i, n] of (obj.nodes as unknown[]).entries()) {\n const err = validateNode(n, i);\n if (err) return err;\n }\n for (const [i, e] of (obj.edges as unknown[]).entries()) {\n const err = validateEdge(e, i);\n if (err) return err;\n }\n return null;\n}\n\nfunction loadIntoStore(stores: WorkspaceStores, data: GraphInput) {\n const labelCounts: Record<string, number> = {};\n for (const n of data.nodes) {\n labelCounts[n.label_] = (labelCounts[n.label_] ?? 0) + 1;\n }\n const palette = buildColorPalette(labelCounts);\n const nodes = enrichNodes(data.nodes, palette);\n const edges = enrichEdges(data.edges, palette);\n stores.graphFilter.getState().resetFilters();\n stores.graphData.getState().setQueryData(nodes, edges, {\n nodes: nodes.length,\n edges: edges.length,\n });\n // Signal renderers to re-fit layout after data change\n stores.graphFilter.getState().notifyDataChange();\n}\n\n// Load initial data immediately -- no effect needed, no empty render cycle\nloadIntoStore(PLAYGROUND_STORES, INITIAL_DATA);\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nexport default function PlayGraph(): React.ReactElement {\n const [jsonText, setJsonText] = useState(() =>\n serializeGraphData(INITIAL_DATA),\n );\n const [parseError, setParseError] = useState<string | null>(null);\n\n // Resizable side panel (demonstrates ResizeHandle).\n const [panelWidth, setPanelWidth] = useState(300);\n const rowRef = useRef<HTMLDivElement>(null);\n const startResize = useCallback((e: React.PointerEvent) => {\n e.preventDefault();\n const target = e.currentTarget as HTMLElement;\n target.setPointerCapture(e.pointerId);\n const move = (ev: PointerEvent) => {\n const right = rowRef.current?.getBoundingClientRect().right ?? 0;\n setPanelWidth(Math.min(560, Math.max(200, right - ev.clientX)));\n };\n const up = () => {\n window.removeEventListener(\"pointermove\", move);\n window.removeEventListener(\"pointerup\", up);\n };\n window.addEventListener(\"pointermove\", move);\n window.addEventListener(\"pointerup\", up);\n }, []);\n\n const handleApply = useCallback(() => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonText);\n } catch (e) {\n setParseError(e instanceof Error ? e.message : \"Invalid JSON\");\n return;\n }\n const validationError = validateGraphInput(parsed);\n if (validationError) {\n setParseError(validationError);\n return;\n }\n setParseError(null);\n loadIntoStore(PLAYGROUND_STORES, parsed as GraphInput);\n }, [jsonText]);\n\n const handleReset = useCallback(() => {\n setJsonText(serializeGraphData(INITIAL_DATA));\n setParseError(null);\n loadIntoStore(PLAYGROUND_STORES, INITIAL_DATA);\n }, []);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key === \"Enter\") {\n e.preventDefault();\n handleApply();\n }\n },\n [handleApply],\n );\n\n return (\n <div className=\"space-y-6\">\n <div>\n <h1>Graph Visualization</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n Interactive property graph with dual renderer support. Edit the JSON\n data and apply to re-render.\n </p>\n </div>\n <GraphStoresContext.Provider value={PLAYGROUND_STORES}>\n <Tabs defaultValue=\"graph\" className=\"space-y-3\">\n <TabsList>\n <TabsTrigger value=\"graph\">Graph</TabsTrigger>\n <TabsTrigger value=\"data\">Data</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"graph\" className=\"mt-0\">\n <GraphRendererProvider defaultRenderer=\"cytoscape\">\n <div ref={rowRef} className=\"flex overflow-hidden h-[600px]\">\n <div className=\"flex-1 min-w-0\">\n <GraphVisualizer />\n </div>\n {/* Drag to resize the side panel (ResizeHandle demo). */}\n <ResizeHandle\n orientation=\"vertical\"\n aria-valuenow={panelWidth}\n aria-valuemin={200}\n aria-valuemax={560}\n onPointerDown={startResize}\n onDoubleClick={() => setPanelWidth(300)}\n />\n <div\n style={{ width: panelWidth }}\n className=\"flex-shrink-0 flex flex-col min-h-0\"\n >\n <div className=\"flex-1 min-h-0 overflow-hidden\">\n <ActionPanel />\n </div>\n {/* Accessible DOM mirror (playground demo only): keyboard / AT /\n automation can select the same nodes/edges as the canvas.\n Real consumers render it sr-only (omit `visible`). */}\n <div className=\"shrink-0 px-4 pb-4 pt-2\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2\">\n Accessible list (demo)\n </p>\n <GraphA11yList visible maxHeight={160} />\n </div>\n </div>\n </div>\n </GraphRendererProvider>\n </TabsContent>\n\n <TabsContent value=\"data\" className=\"mt-0\">\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <Button size=\"sm\" onClick={handleApply}>\n <Play className=\"h-4 w-4 mr-1\" />\n Apply\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={handleReset}>\n <RotateCcw className=\"h-4 w-4 mr-1\" />\n Reset\n </Button>\n <span className=\"text-xs text-muted-foreground ml-2\">\n Cmd+Enter to apply\n </span>\n </div>\n {parseError && (\n <Alert variant=\"destructive\">\n <AlertDescription>{parseError}</AlertDescription>\n </Alert>\n )}\n <textarea\n value={jsonText}\n onChange={(e) => setJsonText(e.target.value)}\n onKeyDown={handleKeyDown}\n spellCheck={false}\n className=\"w-full h-[600px] font-mono text-xs p-3 border rounded-lg bg-muted/30 resize-y focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n </div>\n </TabsContent>\n </Tabs>\n </GraphStoresContext.Provider>\n </div>\n );\n}\n"],"mappings":";AASA,SAAgB,aAAa,QAAQ,gBAAgB;AACrD;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,MAAM,iBAAiB;AAoT1B,SACE,KADF;AA9SN,IAAM,aAA6B;AAAA,EACjC;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,aAAa,SAAS,MAAM,UAAU,aAAa;AAAA,EAC1E;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,eAAe,WAAW,GAAG;AAAA,EACpD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,aAAa,WAAW,GAAG;AAAA,EAClD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,SAAS,WAAW,GAAG;AAAA,EAC9C;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,cAAc,MAAM,MAAM;AAAA,EACjD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,aAAa,MAAM,iBAAiB;AAAA,EAC3D;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,eAAe,MAAM,MAAM;AAAA,EAClD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,eAAe,MAAM,WAAW;AAAA,EACvD;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,eAAe,MAAM,kBAAkB;AAAA,EAC9D;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,eAAe,MAAM,kBAAkB;AAAA,EAC9D;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,cAAc,SAAS,OAAO,QAAQ,KAAK;AAAA,EAClE;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,aAAa,EAAE,MAAM,gBAAgB,SAAS,OAAO,QAAQ,OAAO;AAAA,EACtE;AACF;AAEA,IAAM,aAA6B;AAAA,EACjC;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,EAAE,OAAO,KAAK;AAAA,EAC7B;AAAA,EACA,EAAE,KAAK,KAAK,QAAQ,cAAc,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC,EAAE;AAAA,EAC5E,EAAE,KAAK,KAAK,QAAQ,cAAc,WAAW,GAAG,SAAS,GAAG,aAAa,CAAC,EAAE;AAAA,EAC5E;AAAA,IACE,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,IACT,aAAa,CAAC;AAAA,EAChB;AAAA,EACA,EAAE,KAAK,KAAK,QAAQ,QAAQ,WAAW,GAAG,SAAS,IAAI,aAAa,CAAC,EAAE;AAAA,EACvE,EAAE,KAAK,KAAK,QAAQ,QAAQ,WAAW,GAAG,SAAS,IAAI,aAAa,CAAC,EAAE;AACzE;AASA,SAAS,yBAA0C;AACjD,QAAM,cAAc,uBAAuB;AAC3C,QAAM,YAAY,qBAAqB;AACvC,QAAM,YAAY,qBAAqB;AACvC,QAAM,aAAa,sBAAsB;AACzC,QAAM,mBAAmB,4BAA4B;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA,YAAY,aAAa,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,UAAU,MAAM;AAAA,IACjE,UAAU,YAAY,CAAC;AAAA,EACzB,CAAC;AACD,SAAO,EAAE,aAAa,WAAW,kBAAkB,UAAU;AAC/D;AAEA,IAAM,oBAAoB,uBAAuB;AAWjD,IAAM,eAA2B,EAAE,OAAO,YAAY,OAAO,WAAW;AAExE,SAAS,mBAAmB,MAA0B;AACpD,SAAO,KAAK,UAAU,MAAM,MAAM,CAAC;AACrC;AAEA,SAAS,aAAa,GAAY,GAA0B;AAC1D,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO,SAAS,CAAC;AAClD,QAAM,OAAO;AACb,MAAI,KAAK,QAAQ,OAAW,QAAO,SAAS,CAAC;AAC7C,MAAI,OAAO,KAAK,WAAW,SAAU,QAAO,SAAS,CAAC;AACtD,SAAO;AACT;AAEA,SAAS,aAAa,GAAY,GAA0B;AAC1D,MAAI,CAAC,KAAK,OAAO,MAAM,SAAU,QAAO,SAAS,CAAC;AAClD,QAAM,OAAO;AACb,MAAI,KAAK,QAAQ,OAAW,QAAO,SAAS,CAAC;AAC7C,MAAI,OAAO,KAAK,WAAW,SAAU,QAAO,SAAS,CAAC;AACtD,MAAI,KAAK,cAAc,OAAW,QAAO,SAAS,CAAC;AACnD,MAAI,KAAK,YAAY,OAAW,QAAO,SAAS,CAAC;AACjD,SAAO;AACT;AAGA,SAAS,mBAAmB,OAA+B;AACzD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,QAAM,MAAM;AACZ,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,EAAG,QAAO;AACtC,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,EAAG,QAAO;AACtC,aAAW,CAAC,GAAG,CAAC,KAAM,IAAI,MAAoB,QAAQ,GAAG;AACvD,UAAM,MAAM,aAAa,GAAG,CAAC;AAC7B,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,aAAW,CAAC,GAAG,CAAC,KAAM,IAAI,MAAoB,QAAQ,GAAG;AACvD,UAAM,MAAM,aAAa,GAAG,CAAC;AAC7B,QAAI,IAAK,QAAO;AAAA,EAClB;AACA,SAAO;AACT;AAEA,SAAS,cAAc,QAAyB,MAAkB;AAChE,QAAM,cAAsC,CAAC;AAC7C,aAAW,KAAK,KAAK,OAAO;AAC1B,gBAAY,EAAE,MAAM,KAAK,YAAY,EAAE,MAAM,KAAK,KAAK;AAAA,EACzD;AACA,QAAM,UAAU,kBAAkB,WAAW;AAC7C,QAAM,QAAQ,YAAY,KAAK,OAAO,OAAO;AAC7C,QAAM,QAAQ,YAAY,KAAK,OAAO,OAAO;AAC7C,SAAO,YAAY,SAAS,EAAE,aAAa;AAC3C,SAAO,UAAU,SAAS,EAAE,aAAa,OAAO,OAAO;AAAA,IACrD,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,EACf,CAAC;AAED,SAAO,YAAY,SAAS,EAAE,iBAAiB;AACjD;AAGA,cAAc,mBAAmB,YAAY;AAM9B,SAAR,YAAiD;AACtD,QAAM,CAAC,UAAU,WAAW,IAAI;AAAA,IAAS,MACvC,mBAAmB,YAAY;AAAA,EACjC;AACA,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAGhE,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,GAAG;AAChD,QAAM,SAAS,OAAuB,IAAI;AAC1C,QAAM,cAAc,YAAY,CAAC,MAA0B;AACzD,MAAE,eAAe;AACjB,UAAM,SAAS,EAAE;AACjB,WAAO,kBAAkB,EAAE,SAAS;AACpC,UAAM,OAAO,CAAC,OAAqB;AACjC,YAAM,QAAQ,OAAO,SAAS,sBAAsB,EAAE,SAAS;AAC/D,oBAAc,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,QAAQ,GAAG,OAAO,CAAC,CAAC;AAAA,IAChE;AACA,UAAM,KAAK,MAAM;AACf,aAAO,oBAAoB,eAAe,IAAI;AAC9C,aAAO,oBAAoB,aAAa,EAAE;AAAA,IAC5C;AACA,WAAO,iBAAiB,eAAe,IAAI;AAC3C,WAAO,iBAAiB,aAAa,EAAE;AAAA,EACzC,GAAG,CAAC,CAAC;AAEL,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,QAAQ;AAAA,IAC9B,SAAS,GAAG;AACV,oBAAc,aAAa,QAAQ,EAAE,UAAU,cAAc;AAC7D;AAAA,IACF;AACA,UAAM,kBAAkB,mBAAmB,MAAM;AACjD,QAAI,iBAAiB;AACnB,oBAAc,eAAe;AAC7B;AAAA,IACF;AACA,kBAAc,IAAI;AAClB,kBAAc,mBAAmB,MAAoB;AAAA,EACvD,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,cAAc,YAAY,MAAM;AACpC,gBAAY,mBAAmB,YAAY,CAAC;AAC5C,kBAAc,IAAI;AAClB,kBAAc,mBAAmB,YAAY;AAAA,EAC/C,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgB;AAAA,IACpB,CAAC,MAA2B;AAC1B,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAS;AACjD,UAAE,eAAe;AACjB,oBAAY;AAAA,MACd;AAAA,IACF;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,SACE,qBAAC,SAAI,WAAU,aACb;AAAA,yBAAC,SACC;AAAA,0BAAC,QAAG,iCAAmB;AAAA,MACvB,oBAAC,OAAE,WAAU,sCAAqC,+GAGlD;AAAA,OACF;AAAA,IACA,oBAAC,mBAAmB,UAAnB,EAA4B,OAAO,mBAClC,+BAAC,QAAK,cAAa,SAAQ,WAAU,aACnC;AAAA,2BAAC,YACC;AAAA,4BAAC,eAAY,OAAM,SAAQ,mBAAK;AAAA,QAChC,oBAAC,eAAY,OAAM,QAAO,kBAAI;AAAA,SAChC;AAAA,MAEA,oBAAC,eAAY,OAAM,SAAQ,WAAU,QACnC,8BAAC,yBAAsB,iBAAgB,aACrC,+BAAC,SAAI,KAAK,QAAQ,WAAU,kCAC1B;AAAA,4BAAC,SAAI,WAAU,kBACb,8BAAC,mBAAgB,GACnB;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,aAAY;AAAA,YACZ,iBAAe;AAAA,YACf,iBAAe;AAAA,YACf,iBAAe;AAAA,YACf,eAAe;AAAA,YACf,eAAe,MAAM,cAAc,GAAG;AAAA;AAAA,QACxC;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,EAAE,OAAO,WAAW;AAAA,YAC3B,WAAU;AAAA,YAEV;AAAA,kCAAC,SAAI,WAAU,kCACb,8BAAC,eAAY,GACf;AAAA,cAIA,qBAAC,SAAI,WAAU,2BACb;AAAA,oCAAC,OAAE,WAAU,0EAAyE,oCAEtF;AAAA,gBACA,oBAAC,iBAAc,SAAO,MAAC,WAAW,KAAK;AAAA,iBACzC;AAAA;AAAA;AAAA,QACF;AAAA,SACF,GACF,GACF;AAAA,MAEA,oBAAC,eAAY,OAAM,QAAO,WAAU,QAClC,+BAAC,SAAI,WAAU,aACb;AAAA,6BAAC,SAAI,WAAU,2BACb;AAAA,+BAAC,UAAO,MAAK,MAAK,SAAS,aACzB;AAAA,gCAAC,QAAK,WAAU,gBAAe;AAAA,YAAE;AAAA,aAEnC;AAAA,UACA,qBAAC,UAAO,MAAK,MAAK,SAAQ,WAAU,SAAS,aAC3C;AAAA,gCAAC,aAAU,WAAU,gBAAe;AAAA,YAAE;AAAA,aAExC;AAAA,UACA,oBAAC,UAAK,WAAU,sCAAqC,gCAErD;AAAA,WACF;AAAA,QACC,cACC,oBAAC,SAAM,SAAQ,eACb,8BAAC,oBAAkB,sBAAW,GAChC;AAAA,QAEF;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,YAAY,EAAE,OAAO,KAAK;AAAA,YAC3C,WAAW;AAAA,YACX,YAAY;AAAA,YACZ,WAAU;AAAA;AAAA,QACZ;AAAA,SACF,GACF;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
@@ -229,7 +229,26 @@ function PlayShellApi() {
229
229
  children: "open() \u2014 full coverage"
230
230
  }
231
231
  ),
232
- /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: () => panel.close(), children: "close()" })
232
+ /* @__PURE__ */ jsx(
233
+ Button,
234
+ {
235
+ variant: "outline",
236
+ size: "sm",
237
+ onClick: () => panel.open({
238
+ title: "Resizable Panel",
239
+ description: "Drag the left edge to resize. Double-click to reset.",
240
+ content: /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", children: [
241
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Resizable panel" }),
242
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "resizable=true \xB7 defaultWidth=640 \xB7 minWidth=320 \xB7 maxWidth=1000" })
243
+ ] }),
244
+ width: "default",
245
+ resizable: true,
246
+ minWidth: 320,
247
+ maxWidth: 1e3
248
+ }),
249
+ children: "open() \u2014 resizable"
250
+ }
251
+ )
233
252
  ] })
234
253
  }
235
254
  ),
@@ -319,4 +338,4 @@ function PlayShellApi() {
319
338
  export {
320
339
  PlayShellApi
321
340
  };
322
- //# sourceMappingURL=chunk-4S2ITDB3.js.map
341
+ //# sourceMappingURL=chunk-CQ6PGFRV.js.map