@component-anatomy/storybook 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ Storybook addon that adds an **Anatomy** panel — an interactive part list sync
5
5
  - Hover a part in the panel → the element is highlighted in the canvas
6
6
  - Hover a `data-part` element in the canvas → the panel entry activates
7
7
  - The same table renders **inside MDX** via `@component-anatomy/storybook/blocks`
8
- - Works with **Storybook 9 and 10**, any renderer (React, Vue, HTML, Web Components…)
8
+ - Works with **Storybook 10 and 11**, any renderer (React, Vue, HTML, Web Components…)
9
9
 
10
10
  ## Install
11
11
 
@@ -13,6 +13,9 @@ Storybook addon that adds an **Anatomy** panel — an interactive part list sync
13
13
  npm install --save-dev @component-anatomy/storybook
14
14
  ```
15
15
 
16
+ Register the addon in `.storybook/main.ts`. This is what loads the **Anatomy**
17
+ panel into the Storybook manager:
18
+
16
19
  ```ts
17
20
  // .storybook/main.ts
18
21
  export default {
@@ -20,6 +23,34 @@ export default {
20
23
  };
21
24
  ```
22
25
 
26
+ ### If your `preview.ts` uses CSF Next
27
+
28
+ CSF Next is the default story format in Storybook 11. A `preview.ts` built with
29
+ `definePreview` composes **only** the addons it lists, and Storybook drops the
30
+ preview annotations that `main.ts` would otherwise contribute — so the addon
31
+ also has to be registered there, or the canvas decorator never mounts and the
32
+ panel stays empty:
33
+
34
+ ```ts
35
+ // .storybook/preview.ts
36
+
37
+ // Replace your-framework with the framework you are using (e.g. react-vite, nextjs-vite)
38
+ import { definePreview } from '@storybook/your-framework';
39
+ import componentAnatomy from '@component-anatomy/storybook';
40
+
41
+ export default definePreview({
42
+ // ...rest of preview
43
+ addons: [componentAnatomy()], // 👈 register the addon here
44
+ });
45
+ ```
46
+
47
+ Registering in both places is correct and safe: the two paths are mutually
48
+ exclusive, so the decorator is composed exactly once either way. A `preview.ts`
49
+ that is still a plain object needs only the `main.ts` entry above.
50
+
51
+ Doing so also types `parameters.anatomy` across that preview's metas and
52
+ stories, so the shape below is checked for you.
53
+
23
54
  ## Use
24
55
 
25
56
  Annotate your story's DOM with `data-part` and add the `anatomy` parameter:
@@ -60,40 +91,76 @@ renders MDX), which stays an optional peer dependency — the panel above works
60
91
  without it.
61
92
 
62
93
  ```mdx
63
- import { Canvas, Meta } from '@storybook/addon-docs/blocks';
94
+ import { Meta } from '@storybook/addon-docs/blocks';
64
95
  import { Anatomy } from '@component-anatomy/storybook/blocks';
65
96
 
66
97
  import * as ButtonStories from './Button.stories';
67
98
 
68
99
  <Meta of={ButtonStories} />
69
100
 
70
- <Canvas of={ButtonStories.Anatomy} />
71
101
  <Anatomy of={ButtonStories.Anatomy} />
72
102
  ```
73
103
 
74
- Hover sync works both ways, exactly as in the panel.
104
+ `<Anatomy>` renders the canvas and the table together, tightened into one
105
+ pairing, with the canvas's show-code button dropped by default (chrome that
106
+ mostly restates what the table already documents). Hover sync works both ways,
107
+ exactly as in the panel.
75
108
 
76
109
  | Prop | Type | Description |
77
110
  |---|---|---|
78
111
  | `of` | CSF export | The story to document, or a whole CSF module to read the meta's parameters. Omit on an attached docs page (one with `<Meta of={…} />`) to fall back to the page's current story. |
79
112
  | `parts` | `AnatomyPartDefinition[]` | Curated list, overriding both `parameters.anatomy.parts` and auto-discovery. |
80
113
  | `sync` | `boolean` | Two-way hover sync with the rendered story. Default `true`. `false` gives a purely static table. |
114
+ | `sourceState` | `'hidden' \| 'shown' \| 'none'` | Passed to the underlying `<Canvas>`. Default `'none'` — set it to keep the show-code button. |
115
+
116
+ For a hand-placed canvas — a custom layout, or one that keeps its source panel
117
+ some other way — use `<AnatomyTable>` next to your own `<Canvas>` instead. It
118
+ takes the same `of`, `parts` and `sync` props, minus `sourceState`, which is
119
+ `<Canvas>`'s concern:
81
120
 
82
- Two things worth knowing:
121
+ ```mdx
122
+ import { Canvas, Meta } from '@storybook/addon-docs/blocks';
123
+ import { AnatomyTable } from '@component-anatomy/storybook/blocks';
124
+
125
+ import * as ButtonStories from './Button.stories';
83
126
 
84
- - **Auto-discovery needs the story on the page.** Parts are read from the story's
85
- `data-part` attributes as it renders, so a block that relies on discovery needs
86
- a `<Canvas of={} />` (or `<Story of={…} />`) on the same page. A block with an
87
- explicit `parts` list stands on its own.
127
+ <Meta of={ButtonStories} />
128
+
129
+ <Canvas of={ButtonStories.Anatomy} />
130
+ <AnatomyTable of={ButtonStories.Anatomy} />
131
+ ```
132
+
133
+ Two things worth knowing, for either block:
134
+
135
+ - **Auto-discovery needs the story's canvas on the page.** Parts are read from
136
+ the story's `data-part` attributes as it renders, so a block that relies on
137
+ discovery needs a canvas on the same page — `<Anatomy>` brings its own,
138
+ `<AnatomyTable>` needs a `<Canvas of={…} />` (or `<Story of={…} />`) next to
139
+ it. A block with an explicit `parts` list stands on its own.
88
140
  - **A meta has no canvas.** `of={ButtonStories}` reads `meta.parameters.anatomy`,
89
141
  but there is nothing to discover parts from or to sync hover with — give it
90
- `parts` explicitly.
142
+ `parts` explicitly, and use `<AnatomyTable>` rather than `<Anatomy>` since
143
+ there is no story to draw a canvas from.
91
144
 
92
145
  Several blocks can share one page; each talks only to the story it names.
93
146
 
147
+ ## Compatibility
148
+
149
+ | | |
150
+ |---|---|
151
+ | `storybook` | `^10.0.0 \|\| ^11.0.0-0` |
152
+ | `@storybook/addon-docs` | `^10.0.0 \|\| ^11.0.0-0` (optional — only for the MDX blocks) |
153
+ | `react` | `>=18` (optional — only for the MDX blocks) |
154
+ | Node | `>=22.12.0` |
155
+
156
+ The package is ESM-only, like Storybook itself since 9. Storybook 9 is no
157
+ longer supported: CSF Next registration needs `definePreviewAddon`, which
158
+ Storybook only ships from 9.1 onwards, and the addon is built and tested
159
+ against 10 and 11.
160
+
94
161
  ## Example
95
162
 
96
- A complete Storybook 10 setup with Button/Slider/Tabs stories — and two MDX
163
+ A complete Storybook 11 setup with Button/Slider/Tabs stories — and two MDX
97
164
  pages using the `<Anatomy>` block — lives in [`examples/storybook`](https://github.com/julien-deramond/component-anatomy/tree/main/examples/storybook), deployed at https://julien-deramond.github.io/component-anatomy/storybook/.
98
165
 
99
166
  ## Docs
package/dist/blocks.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Docs blocks entry — the `<Anatomy>` block for MDX pages.
2
+ * Docs blocks entry — the `<Anatomy>` and `<AnatomyTable>` blocks for MDX
3
+ * pages.
3
4
  *
4
5
  * Unlike the addon panel, this runs in the **preview iframe**, where
5
6
  * `storybook/manager-api` does not exist. It reaches the story's controller
@@ -8,21 +9,37 @@
8
9
  * mounted the story talk to each other directly, in-frame, with no extra
9
10
  * plumbing.
10
11
  *
12
+ * `<Anatomy>` is the batteries-included block — canvas and table together,
13
+ * laid out for that pairing specifically ([#13](https://github.com/julien-deramond/component-anatomy/issues/13#issuecomment-5475936860)):
14
+ *
11
15
  * ```mdx
12
- * import { Meta, Canvas } from '@storybook/addon-docs/blocks';
16
+ * import { Meta } from '@storybook/addon-docs/blocks';
13
17
  * import { Anatomy } from '@component-anatomy/storybook/blocks';
14
18
  * import * as ButtonStories from './Button.stories';
15
19
  *
16
20
  * <Meta of={ButtonStories} />
17
21
  *
18
- * <Canvas of={ButtonStories.Anatomy} />
19
22
  * <Anatomy of={ButtonStories.Anatomy} />
20
23
  * ```
24
+ *
25
+ * `<AnatomyTable>` is the table alone, for a hand-placed `<Canvas>` or a
26
+ * custom layout:
27
+ *
28
+ * ```mdx
29
+ * import { Canvas, Meta } from '@storybook/addon-docs/blocks';
30
+ * import { AnatomyTable } from '@component-anatomy/storybook/blocks';
31
+ * import * as ButtonStories from './Button.stories';
32
+ *
33
+ * <Meta of={ButtonStories} />
34
+ *
35
+ * <Canvas of={ButtonStories.Anatomy} />
36
+ * <AnatomyTable of={ButtonStories.Anatomy} />
37
+ * ```
21
38
  */
22
39
  import React from 'react';
23
40
  import type { Of } from '@storybook/addon-docs/blocks';
24
41
  import type { AnatomyPartDefinition } from '@component-anatomy/core';
25
- export type AnatomyBlockProps = {
42
+ export type AnatomyTableProps = {
26
43
  /**
27
44
  * The CSF export to document — a story export, or the whole module export
28
45
  * of a CSF file to read the meta's parameters.
@@ -44,5 +61,22 @@ export type AnatomyBlockProps = {
44
61
  */
45
62
  sync?: boolean;
46
63
  };
47
- export declare const Anatomy: React.FC<AnatomyBlockProps>;
64
+ export declare const AnatomyTable: React.FC<AnatomyTableProps>;
65
+ export type AnatomyProps = AnatomyTableProps & {
66
+ /**
67
+ * Passed straight to the underlying `<Canvas>`. `'none'` (the default)
68
+ * drops the show-code button along with the panel it opens — chrome that
69
+ * mostly restates what the anatomy table already documents. Set explicitly
70
+ * to keep it, e.g. for a consumer relying on that source elsewhere.
71
+ */
72
+ sourceState?: 'hidden' | 'shown' | 'none';
73
+ };
74
+ /**
75
+ * The canvas and its anatomy table, paired and tightened for that specific
76
+ * combination ([#13](https://github.com/julien-deramond/component-anatomy/issues/13#issuecomment-5475936860)).
77
+ * For a hand-placed `<Canvas>` — a custom layout, or a source panel this
78
+ * block would otherwise hide — use `<AnatomyTable>` next to your own
79
+ * `<Canvas>` instead.
80
+ */
81
+ export declare const Anatomy: React.FC<AnatomyProps>;
48
82
  //# sourceMappingURL=blocks.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAQrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,EAAE,CAAC;IACR;;;OAGG;IACH,KAAK,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAgBF,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6H/C,CAAC"}
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,8BAA8B,CAAC;AACvD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAQrE,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,EAAE,CAAC;IACR;;;OAGG;IACH,KAAK,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAChC;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAgBF,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6HpD,CAAC;AAwBF,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG;IAC7C;;;;;OAKG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;CAC3C,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,YAAY,CAa1C,CAAC"}
package/dist/blocks.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/blocks.tsx
2
2
  import React2, { useEffect, useState } from "react";
3
3
  import { addons } from "storybook/preview-api";
4
- import { Unstyled, useOf } from "@storybook/addon-docs/blocks";
4
+ import { Canvas, Unstyled, useOf } from "@storybook/addon-docs/blocks";
5
5
 
6
6
  // src/constants.ts
7
7
  var ADDON_ID = "component-anatomy";
@@ -180,7 +180,7 @@ function getChannelSafely() {
180
180
  return null;
181
181
  }
182
182
  }
183
- var Anatomy = ({ of, parts: partsProp, sync = true }) => {
183
+ var AnatomyTable2 = ({ of, parts: partsProp, sync = true }) => {
184
184
  const resolved = useOf(of ?? "story", ["story", "meta"]);
185
185
  const params = resolved.type === "meta" ? resolved.preparedMeta.parameters?.[PARAM_KEY] : resolved.story.parameters?.[PARAM_KEY];
186
186
  const storyId = resolved.type === "story" ? resolved.story.id : void 0;
@@ -246,7 +246,28 @@ var Anatomy = ({ of, parts: partsProp, sync = true }) => {
246
246
  }
247
247
  ));
248
248
  };
249
+ var canvasGapStyleInjected = false;
250
+ function useCanvasGapStyle() {
251
+ useEffect(() => {
252
+ if (canvasGapStyleInjected || typeof document === "undefined") return;
253
+ canvasGapStyleInjected = true;
254
+ const style = document.createElement("style");
255
+ style.dataset.componentAnatomy = "anatomy-block";
256
+ style.textContent = ".ca-anatomy .sbdocs-preview { margin-bottom: 8px; }";
257
+ document.head.appendChild(style);
258
+ }, []);
259
+ }
260
+ var Anatomy = ({
261
+ of,
262
+ parts,
263
+ sync,
264
+ sourceState = "none"
265
+ }) => {
266
+ useCanvasGapStyle();
267
+ return /* @__PURE__ */ React2.createElement("div", { className: "ca-anatomy" }, /* @__PURE__ */ React2.createElement(Canvas, { of, sourceState }), /* @__PURE__ */ React2.createElement(AnatomyTable2, { of, parts, sync }));
268
+ };
249
269
  export {
250
- Anatomy
270
+ Anatomy,
271
+ AnatomyTable2 as AnatomyTable
251
272
  };
252
273
  //# sourceMappingURL=blocks.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/blocks.tsx", "../src/constants.ts", "../src/channel.ts", "../src/AnatomyTable.tsx"],
4
- "sourcesContent": ["/**\n * Docs blocks entry \u2014 the `<Anatomy>` block for MDX pages.\n *\n * Unlike the addon panel, this runs in the **preview iframe**, where\n * `storybook/manager-api` does not exist. It reaches the story's controller\n * over the addon channel instead: `Channel.emit` dispatches to local\n * listeners as well as across transports, so a block and the decorator that\n * mounted the story talk to each other directly, in-frame, with no extra\n * plumbing.\n *\n * ```mdx\n * import { Meta, Canvas } from '@storybook/addon-docs/blocks';\n * import { Anatomy } from '@component-anatomy/storybook/blocks';\n * import * as ButtonStories from './Button.stories';\n *\n * <Meta of={ButtonStories} />\n *\n * <Canvas of={ButtonStories.Anatomy} />\n * <Anatomy of={ButtonStories.Anatomy} />\n * ```\n */\nimport React, { useEffect, useState } from 'react';\nimport { addons } from 'storybook/preview-api';\nimport { Unstyled, useOf } from '@storybook/addon-docs/blocks';\nimport type { Of } from '@storybook/addon-docs/blocks';\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { PartEnterEvent, PartsEvent, StoryScopedEvent } from './channel.js';\nimport { AnatomyCode, AnatomyMessage, AnatomyTable } from './AnatomyTable.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport type AnatomyBlockProps = {\n /**\n * The CSF export to document \u2014 a story export, or the whole module export\n * of a CSF file to read the meta's parameters.\n *\n * Omit it on an attached docs page (one with `<Meta of={...} />`, or an\n * autodocs page) to fall back to the page's current story, mirroring how\n * the other docs blocks resolve `of`.\n */\n of?: Of;\n /**\n * Part list override. Skips both `parameters.anatomy.parts` and\n * auto-discovery \u2014 useful for a hand-curated subset in prose.\n */\n parts?: AnatomyPartDefinition[];\n /**\n * Two-way hover sync with the rendered story. Default: `true`. Set to\n * `false` for a purely static table (also skips auto-discovery, since that\n * arrives over the channel).\n */\n sync?: boolean;\n};\n\n/**\n * `addons.getChannel()` throws when no channel is installed. That should not\n * happen inside a rendered docs page, but an MDX page is user-authored\n * content and a throw here would blank the whole page \u2014 degrade to a static\n * table instead.\n */\nfunction getChannelSafely() {\n try {\n return addons.getChannel();\n } catch {\n return null;\n }\n}\n\nexport const Anatomy: React.FC<AnatomyBlockProps> = ({ of, parts: partsProp, sync = true }) => {\n const resolved = useOf(of ?? 'story', ['story', 'meta']);\n\n const params = (\n resolved.type === 'meta'\n ? resolved.preparedMeta.parameters?.[PARAM_KEY]\n : resolved.story.parameters?.[PARAM_KEY]\n ) as AnatomyParameters | undefined;\n\n // Only a story has a canvas to sync with. `of={SomeStories}` (a meta)\n // documents the component as a whole and can render static parts only.\n const storyId = resolved.type === 'story' ? resolved.story.id : undefined;\n\n const [discovered, setDiscovered] = useState<AnatomyPartDefinition[]>([]);\n const [activeId, setActiveId] = useState<string | null>(null);\n\n const staticParts = partsProp ?? params?.parts;\n // Hover sync is wired even when the parts are static \u2014 an explicit list\n // still wants the canvas to light up. Only discovery depends on the channel.\n const wired = sync && !!storyId;\n\n useEffect(() => {\n setDiscovered([]);\n setActiveId(null);\n if (!wired) return;\n\n const channel = getChannelSafely();\n if (!channel) return;\n\n const onParts = (event: PartsEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n setDiscovered(event.parts);\n };\n const onEnter = (event: PartEnterEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n setActiveId(event.partId);\n };\n const onLeave = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n setActiveId(null);\n };\n\n channel.on(EVENTS.PARTS, onParts);\n channel.on(EVENTS.PART_ENTER, onEnter);\n channel.on(EVENTS.PART_LEAVE, onLeave);\n\n // The block usually mounts before the story below it finishes rendering;\n // the request covers the other order.\n channel.emit(EVENTS.PARTS_REQUEST, { storyId } satisfies StoryScopedEvent);\n\n return () => {\n channel.off(EVENTS.PARTS, onParts);\n channel.off(EVENTS.PART_ENTER, onEnter);\n channel.off(EVENTS.PART_LEAVE, onLeave);\n };\n }, [wired, storyId]);\n\n const parts = staticParts ?? discovered;\n\n const emitHover = (partId: string) => {\n if (!wired) return;\n getChannelSafely()?.emit(EVENTS.HOVER_ITEM, { storyId, partId });\n };\n const emitLeave = () => {\n if (!wired) return;\n getChannelSafely()?.emit(EVENTS.LEAVE_ITEM, { storyId } satisfies StoryScopedEvent);\n };\n\n // `Unstyled` keeps the docs page's prose CSS (`.sbdocs` restyles ul/li/p/\n // code) from reaching the table.\n if (!params && !partsProp) {\n return (\n <Unstyled>\n <AnatomyMessage>\n No anatomy configured. Add <AnatomyCode>parameters.anatomy</AnatomyCode> to the story you\n pass to <AnatomyCode>of</AnatomyCode>, or pass a{' '}\n <AnatomyCode>parts</AnatomyCode> list to this block.\n </AnatomyMessage>\n </Unstyled>\n );\n }\n\n if (params?.disable && !partsProp) {\n return (\n <Unstyled>\n <AnatomyMessage>\n Anatomy is disabled for this story (<AnatomyCode>anatomy.disable</AnatomyCode>).\n </AnatomyMessage>\n </Unstyled>\n );\n }\n\n if (parts.length === 0) {\n return (\n <Unstyled>\n <AnatomyMessage>\n {resolved.type === 'meta' ? (\n <>\n No parts found. A meta has no canvas to discover parts from \u2014 pass a story to{' '}\n <AnatomyCode>of</AnatomyCode>, or list <AnatomyCode>parts</AnatomyCode> explicitly.\n </>\n ) : (\n <>\n No parts found. Auto-discovery reads the rendered story, so make sure it is on this\n page (e.g. with a <AnatomyCode>{'<Canvas of={\u2026} />'}</AnatomyCode> block above), or\n list <AnatomyCode>parts</AnatomyCode> explicitly.\n </>\n )}\n </AnatomyMessage>\n </Unstyled>\n );\n }\n\n return (\n <Unstyled>\n <AnatomyTable\n parts={parts}\n activeId={activeId}\n preset={params?.preset}\n theme={params?.theme}\n onItemEnter={emitHover}\n onItemLeave={emitLeave}\n />\n </Unstyled>\n );\n};\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n", "/**\n * The anatomy part table \u2014 pure presentation, no Storybook imports.\n *\n * This module is deliberately free of `storybook/manager-api` and\n * `storybook/preview-api`: it is bundled into *both* the manager entry (the\n * addon panel) and the blocks entry (the `<Anatomy>` MDX doc block), which\n * run in different runtimes and cannot share either of those APIs. Only the\n * data acquisition differs between the two; the markup must not.\n */\nimport React from 'react';\nimport { useTheme } from 'storybook/theming';\nimport { resolvePanelAccent } from '@component-anatomy/core';\nimport type {\n AnatomyPartDefinition,\n AnatomyPresetName,\n AnatomyTheme,\n} from '@component-anatomy/core';\n\n/**\n * The subset of Storybook's theme this table reads. Typed loosely on purpose:\n * `useTheme()` resolves to an empty `Theme` interface (Emotion augmentation),\n * and outside a ThemeProvider it returns `{}` \u2014 so every read is optional and\n * every value has a literal fallback below.\n */\ntype PartialStorybookTheme = {\n fgColor?: { default?: string; muted?: string };\n bgColor?: { default?: string; muted?: string };\n borderColor?: { default?: string };\n typography?: { fonts?: { base?: string; mono?: string } };\n};\n\nconst FONT_BASE_FALLBACK =\n '\"Nunito Sans\", -apple-system, \".SFNSText-Regular\", \"San Francisco\", BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Helvetica, Arial, sans-serif';\nconst FONT_MONO_FALLBACK = 'ui-monospace, \"Cascadia Code\", \"Fira Mono\", monospace';\n\n/**\n * Resolves the table's chrome colors from Storybook's theme so the same\n * markup reads correctly in the manager panel *and* in a docs page under a\n * dark theme.\n *\n * `surfaceBg`/`surfaceFg` are not painted anywhere \u2014 they are what the accent\n * is measured against in {@link useAccent}. They carry Storybook's own light\n * defaults as fallbacks so the measurement stays meaningful outside a\n * ThemeProvider, where `useTheme()` returns `{}`.\n */\nfunction usePalette() {\n const theme = useTheme() as PartialStorybookTheme;\n return {\n text: theme.fgColor?.default,\n muted: theme.fgColor?.muted ?? '#6b7280',\n mutedBg: theme.bgColor?.muted ?? 'rgba(0,0,0,0.06)',\n border: theme.borderColor?.default ?? 'rgba(0,0,0,0.08)',\n surfaceBg: theme.bgColor?.default ?? '#ffffff',\n surfaceFg: theme.fgColor?.default ?? '#2e3438',\n fontBase: theme.typography?.fonts?.base ?? FONT_BASE_FALLBACK,\n fontMono: theme.typography?.fonts?.mono ?? FONT_MONO_FALLBACK,\n };\n}\n\ntype Palette = ReturnType<typeof usePalette>;\n\n/**\n * The accent for the active row \u2014 the story's own `{ preset, theme }`, so the\n * table agrees with the overlays it describes, lifted to WCAG AA against the\n * surface it is painted on when the two disagree.\n *\n * The check is not optional polish: `preset: 'contrast'` is black on yellow\n * and the Storybook manager is dark by default, so the preset that exists for\n * users who need contrast is exactly the one that would land at 1.3:1 here.\n */\nfunction useAccent(palette: Palette, preset?: AnatomyPresetName, theme?: AnatomyTheme): string {\n return resolvePanelAccent(preset, theme, {\n background: palette.surfaceBg,\n foreground: palette.surfaceFg,\n });\n}\n\nconst makeStyles = (p: Palette): Record<string, React.CSSProperties> => ({\n container: {\n padding: '12px 16px',\n fontFamily: p.fontBase,\n fontSize: 13,\n lineHeight: 1.5,\n color: p.text,\n },\n empty: {\n color: p.muted,\n margin: 0,\n },\n code: {\n fontFamily: p.fontMono,\n fontSize: '0.85em',\n background: p.mutedBg,\n borderRadius: 3,\n padding: '1px 5px',\n },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n margin: 0,\n padding: 0,\n listStyle: 'none',\n },\n entry: {\n padding: '8px 10px',\n borderRadius: 6,\n border: '1px solid transparent',\n cursor: 'default',\n outline: 'none',\n transition: 'background 120ms ease, border-color 120ms ease',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n },\n indicator: {\n width: 8,\n height: 8,\n borderRadius: '50%',\n border: `2px solid ${p.border}`,\n flexShrink: 0,\n transition: 'background 120ms ease, border-color 120ms ease',\n },\n name: {\n fontWeight: 700,\n flex: 1,\n },\n id: {\n fontFamily: p.fontMono,\n fontSize: 10,\n padding: '1px 6px',\n borderRadius: 4,\n background: p.mutedBg,\n border: `1px solid ${p.border}`,\n color: p.muted,\n flexShrink: 0,\n },\n description: {\n margin: '4px 0 0',\n paddingLeft: 16,\n color: p.muted,\n },\n});\n\n/**\n * An inline `<code>` styled for the surrounding message text. Exported so\n * both runtimes can compose their own empty-state copy without duplicating\n * the style object.\n */\nexport const AnatomyCode: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const styles = makeStyles(usePalette());\n return <code style={styles.code}>{children}</code>;\n};\n\n/** A padded, muted paragraph \u2014 used for every \"nothing to show\" state. */\nexport const AnatomyMessage: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const styles = makeStyles(usePalette());\n return (\n <div style={styles.container}>\n <p style={styles.empty}>{children}</p>\n </div>\n );\n};\n\nexport type AnatomyTableProps = {\n /** Parts to list, in the order they should be shown. */\n parts: AnatomyPartDefinition[];\n /** Id of the part currently highlighted in the canvas, if any. */\n activeId?: string | null;\n /** The story's `anatomy.preset` \u2014 the active row follows it. */\n preset?: AnatomyPresetName;\n /** The story's `anatomy.theme` \u2014 token overrides on top of the preset. */\n theme?: AnatomyTheme;\n /** Called when the user hovers or focuses an entry. */\n onItemEnter?: (partId: string) => void;\n /** Called when the user leaves or blurs an entry. */\n onItemLeave?: () => void;\n};\n\nexport const AnatomyTable: React.FC<AnatomyTableProps> = ({\n parts,\n activeId = null,\n preset,\n theme,\n onItemEnter,\n onItemLeave,\n}) => {\n const palette = usePalette();\n const styles = makeStyles(palette);\n const accent = useAccent(palette, preset, theme);\n\n return (\n <div style={styles.container}>\n <ul style={styles.list} role=\"list\" aria-label=\"Anatomy parts\">\n {parts.map((part) => {\n const active = activeId === part.id;\n return (\n <li\n key={part.id}\n role=\"listitem\"\n tabIndex={0}\n aria-label={part.name}\n style={{\n ...styles.entry,\n background: active ? `color-mix(in srgb, ${accent} 7%, transparent)` : undefined,\n borderColor: active\n ? `color-mix(in srgb, ${accent} 20%, transparent)`\n : 'transparent',\n }}\n onMouseEnter={() => onItemEnter?.(part.id)}\n onMouseLeave={() => onItemLeave?.()}\n onFocus={() => onItemEnter?.(part.id)}\n onBlur={() => onItemLeave?.()}\n >\n <div style={styles.header}>\n <span\n aria-hidden=\"true\"\n style={{\n ...styles.indicator,\n background: active ? accent : undefined,\n borderColor: active ? accent : palette.border,\n }}\n />\n <span style={{ ...styles.name, color: active ? accent : undefined }}>\n {part.name}\n </span>\n <code style={styles.id}>{part.id}</code>\n </div>\n {part.description && <p style={styles.description}>{part.description}</p>}\n </li>\n );\n })}\n </ul>\n </div>\n );\n};\n"],
5
- "mappings": "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",
6
- "names": ["React", "React"]
4
+ "sourcesContent": ["/**\n * Docs blocks entry \u2014 the `<Anatomy>` and `<AnatomyTable>` blocks for MDX\n * pages.\n *\n * Unlike the addon panel, this runs in the **preview iframe**, where\n * `storybook/manager-api` does not exist. It reaches the story's controller\n * over the addon channel instead: `Channel.emit` dispatches to local\n * listeners as well as across transports, so a block and the decorator that\n * mounted the story talk to each other directly, in-frame, with no extra\n * plumbing.\n *\n * `<Anatomy>` is the batteries-included block \u2014 canvas and table together,\n * laid out for that pairing specifically ([#13](https://github.com/julien-deramond/component-anatomy/issues/13#issuecomment-5475936860)):\n *\n * ```mdx\n * import { Meta } from '@storybook/addon-docs/blocks';\n * import { Anatomy } from '@component-anatomy/storybook/blocks';\n * import * as ButtonStories from './Button.stories';\n *\n * <Meta of={ButtonStories} />\n *\n * <Anatomy of={ButtonStories.Anatomy} />\n * ```\n *\n * `<AnatomyTable>` is the table alone, for a hand-placed `<Canvas>` or a\n * custom layout:\n *\n * ```mdx\n * import { Canvas, Meta } from '@storybook/addon-docs/blocks';\n * import { AnatomyTable } from '@component-anatomy/storybook/blocks';\n * import * as ButtonStories from './Button.stories';\n *\n * <Meta of={ButtonStories} />\n *\n * <Canvas of={ButtonStories.Anatomy} />\n * <AnatomyTable of={ButtonStories.Anatomy} />\n * ```\n */\nimport React, { useEffect, useState } from 'react';\nimport { addons } from 'storybook/preview-api';\nimport { Canvas, Unstyled, useOf } from '@storybook/addon-docs/blocks';\nimport type { Of } from '@storybook/addon-docs/blocks';\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { PartEnterEvent, PartsEvent, StoryScopedEvent } from './channel.js';\nimport { AnatomyCode, AnatomyMessage, AnatomyTable as AnatomyTablePrimitive } from './AnatomyTable.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport type AnatomyTableProps = {\n /**\n * The CSF export to document \u2014 a story export, or the whole module export\n * of a CSF file to read the meta's parameters.\n *\n * Omit it on an attached docs page (one with `<Meta of={...} />`, or an\n * autodocs page) to fall back to the page's current story, mirroring how\n * the other docs blocks resolve `of`.\n */\n of?: Of;\n /**\n * Part list override. Skips both `parameters.anatomy.parts` and\n * auto-discovery \u2014 useful for a hand-curated subset in prose.\n */\n parts?: AnatomyPartDefinition[];\n /**\n * Two-way hover sync with the rendered story. Default: `true`. Set to\n * `false` for a purely static table (also skips auto-discovery, since that\n * arrives over the channel).\n */\n sync?: boolean;\n};\n\n/**\n * `addons.getChannel()` throws when no channel is installed. That should not\n * happen inside a rendered docs page, but an MDX page is user-authored\n * content and a throw here would blank the whole page \u2014 degrade to a static\n * table instead.\n */\nfunction getChannelSafely() {\n try {\n return addons.getChannel();\n } catch {\n return null;\n }\n}\n\nexport const AnatomyTable: React.FC<AnatomyTableProps> = ({ of, parts: partsProp, sync = true }) => {\n const resolved = useOf(of ?? 'story', ['story', 'meta']);\n\n const params = (\n resolved.type === 'meta'\n ? resolved.preparedMeta.parameters?.[PARAM_KEY]\n : resolved.story.parameters?.[PARAM_KEY]\n ) as AnatomyParameters | undefined;\n\n // Only a story has a canvas to sync with. `of={SomeStories}` (a meta)\n // documents the component as a whole and can render static parts only.\n const storyId = resolved.type === 'story' ? resolved.story.id : undefined;\n\n const [discovered, setDiscovered] = useState<AnatomyPartDefinition[]>([]);\n const [activeId, setActiveId] = useState<string | null>(null);\n\n const staticParts = partsProp ?? params?.parts;\n // Hover sync is wired even when the parts are static \u2014 an explicit list\n // still wants the canvas to light up. Only discovery depends on the channel.\n const wired = sync && !!storyId;\n\n useEffect(() => {\n setDiscovered([]);\n setActiveId(null);\n if (!wired) return;\n\n const channel = getChannelSafely();\n if (!channel) return;\n\n const onParts = (event: PartsEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n setDiscovered(event.parts);\n };\n const onEnter = (event: PartEnterEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n setActiveId(event.partId);\n };\n const onLeave = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n setActiveId(null);\n };\n\n channel.on(EVENTS.PARTS, onParts);\n channel.on(EVENTS.PART_ENTER, onEnter);\n channel.on(EVENTS.PART_LEAVE, onLeave);\n\n // The block usually mounts before the story below it finishes rendering;\n // the request covers the other order.\n channel.emit(EVENTS.PARTS_REQUEST, { storyId } satisfies StoryScopedEvent);\n\n return () => {\n channel.off(EVENTS.PARTS, onParts);\n channel.off(EVENTS.PART_ENTER, onEnter);\n channel.off(EVENTS.PART_LEAVE, onLeave);\n };\n }, [wired, storyId]);\n\n const parts = staticParts ?? discovered;\n\n const emitHover = (partId: string) => {\n if (!wired) return;\n getChannelSafely()?.emit(EVENTS.HOVER_ITEM, { storyId, partId });\n };\n const emitLeave = () => {\n if (!wired) return;\n getChannelSafely()?.emit(EVENTS.LEAVE_ITEM, { storyId } satisfies StoryScopedEvent);\n };\n\n // `Unstyled` keeps the docs page's prose CSS (`.sbdocs` restyles ul/li/p/\n // code) from reaching the table.\n if (!params && !partsProp) {\n return (\n <Unstyled>\n <AnatomyMessage>\n No anatomy configured. Add <AnatomyCode>parameters.anatomy</AnatomyCode> to the story you\n pass to <AnatomyCode>of</AnatomyCode>, or pass a{' '}\n <AnatomyCode>parts</AnatomyCode> list to this block.\n </AnatomyMessage>\n </Unstyled>\n );\n }\n\n if (params?.disable && !partsProp) {\n return (\n <Unstyled>\n <AnatomyMessage>\n Anatomy is disabled for this story (<AnatomyCode>anatomy.disable</AnatomyCode>).\n </AnatomyMessage>\n </Unstyled>\n );\n }\n\n if (parts.length === 0) {\n return (\n <Unstyled>\n <AnatomyMessage>\n {resolved.type === 'meta' ? (\n <>\n No parts found. A meta has no canvas to discover parts from \u2014 pass a story to{' '}\n <AnatomyCode>of</AnatomyCode>, or list <AnatomyCode>parts</AnatomyCode> explicitly.\n </>\n ) : (\n <>\n No parts found. Auto-discovery reads the rendered story, so make sure it is on this\n page (e.g. with a <AnatomyCode>{'<Canvas of={\u2026} />'}</AnatomyCode> block above), or\n list <AnatomyCode>parts</AnatomyCode> explicitly.\n </>\n )}\n </AnatomyMessage>\n </Unstyled>\n );\n }\n\n return (\n <Unstyled>\n <AnatomyTablePrimitive\n parts={parts}\n activeId={activeId}\n preset={params?.preset}\n theme={params?.theme}\n onItemEnter={emitHover}\n onItemLeave={emitLeave}\n />\n </Unstyled>\n );\n};\n\n/**\n * Injects the rule that tightens `<Anatomy>`'s canvas-to-table gap, once per\n * page \u2014 a module-level flag rather than `useState`, so mounting a second\n * `<Anatomy>` block doesn't append the rule again. Scoped under\n * `.ca-anatomy` so it never touches a bare `<Canvas>` placed by the consumer.\n *\n * Self-injected rather than asked of the consumer's `preview-head.html`: the\n * block owns both elements it is bridging, so the gap between them is its\n * concern, not config the consumer should have to wire up.\n */\nlet canvasGapStyleInjected = false;\nfunction useCanvasGapStyle() {\n useEffect(() => {\n if (canvasGapStyleInjected || typeof document === 'undefined') return;\n canvasGapStyleInjected = true;\n const style = document.createElement('style');\n style.dataset.componentAnatomy = 'anatomy-block';\n style.textContent = '.ca-anatomy .sbdocs-preview { margin-bottom: 8px; }';\n document.head.appendChild(style);\n }, []);\n}\n\nexport type AnatomyProps = AnatomyTableProps & {\n /**\n * Passed straight to the underlying `<Canvas>`. `'none'` (the default)\n * drops the show-code button along with the panel it opens \u2014 chrome that\n * mostly restates what the anatomy table already documents. Set explicitly\n * to keep it, e.g. for a consumer relying on that source elsewhere.\n */\n sourceState?: 'hidden' | 'shown' | 'none';\n};\n\n/**\n * The canvas and its anatomy table, paired and tightened for that specific\n * combination ([#13](https://github.com/julien-deramond/component-anatomy/issues/13#issuecomment-5475936860)).\n * For a hand-placed `<Canvas>` \u2014 a custom layout, or a source panel this\n * block would otherwise hide \u2014 use `<AnatomyTable>` next to your own\n * `<Canvas>` instead.\n */\nexport const Anatomy: React.FC<AnatomyProps> = ({\n of,\n parts,\n sync,\n sourceState = 'none',\n}) => {\n useCanvasGapStyle();\n return (\n <div className=\"ca-anatomy\">\n <Canvas of={of} sourceState={sourceState} />\n <AnatomyTable of={of} parts={parts} sync={sync} />\n </div>\n );\n};\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n", "/**\n * The anatomy part table \u2014 pure presentation, no Storybook imports.\n *\n * This module is deliberately free of `storybook/manager-api` and\n * `storybook/preview-api`: it is bundled into *both* the manager entry (the\n * addon panel) and the blocks entry (the `<Anatomy>` MDX doc block), which\n * run in different runtimes and cannot share either of those APIs. Only the\n * data acquisition differs between the two; the markup must not.\n */\nimport React from 'react';\nimport { useTheme } from 'storybook/theming';\nimport { resolvePanelAccent } from '@component-anatomy/core';\nimport type {\n AnatomyPartDefinition,\n AnatomyPresetName,\n AnatomyTheme,\n} from '@component-anatomy/core';\n\n/**\n * The subset of Storybook's theme this table reads. Typed loosely on purpose:\n * `useTheme()` resolves to an empty `Theme` interface (Emotion augmentation),\n * and outside a ThemeProvider it returns `{}` \u2014 so every read is optional and\n * every value has a literal fallback below.\n */\ntype PartialStorybookTheme = {\n fgColor?: { default?: string; muted?: string };\n bgColor?: { default?: string; muted?: string };\n borderColor?: { default?: string };\n typography?: { fonts?: { base?: string; mono?: string } };\n};\n\nconst FONT_BASE_FALLBACK =\n '\"Nunito Sans\", -apple-system, \".SFNSText-Regular\", \"San Francisco\", BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Helvetica, Arial, sans-serif';\nconst FONT_MONO_FALLBACK = 'ui-monospace, \"Cascadia Code\", \"Fira Mono\", monospace';\n\n/**\n * Resolves the table's chrome colors from Storybook's theme so the same\n * markup reads correctly in the manager panel *and* in a docs page under a\n * dark theme.\n *\n * `surfaceBg`/`surfaceFg` are not painted anywhere \u2014 they are what the accent\n * is measured against in {@link useAccent}. They carry Storybook's own light\n * defaults as fallbacks so the measurement stays meaningful outside a\n * ThemeProvider, where `useTheme()` returns `{}`.\n */\nfunction usePalette() {\n const theme = useTheme() as PartialStorybookTheme;\n return {\n text: theme.fgColor?.default,\n muted: theme.fgColor?.muted ?? '#6b7280',\n mutedBg: theme.bgColor?.muted ?? 'rgba(0,0,0,0.06)',\n border: theme.borderColor?.default ?? 'rgba(0,0,0,0.08)',\n surfaceBg: theme.bgColor?.default ?? '#ffffff',\n surfaceFg: theme.fgColor?.default ?? '#2e3438',\n fontBase: theme.typography?.fonts?.base ?? FONT_BASE_FALLBACK,\n fontMono: theme.typography?.fonts?.mono ?? FONT_MONO_FALLBACK,\n };\n}\n\ntype Palette = ReturnType<typeof usePalette>;\n\n/**\n * The accent for the active row \u2014 the story's own `{ preset, theme }`, so the\n * table agrees with the overlays it describes, lifted to WCAG AA against the\n * surface it is painted on when the two disagree.\n *\n * The check is not optional polish: `preset: 'contrast'` is black on yellow\n * and the Storybook manager is dark by default, so the preset that exists for\n * users who need contrast is exactly the one that would land at 1.3:1 here.\n */\nfunction useAccent(palette: Palette, preset?: AnatomyPresetName, theme?: AnatomyTheme): string {\n return resolvePanelAccent(preset, theme, {\n background: palette.surfaceBg,\n foreground: palette.surfaceFg,\n });\n}\n\nconst makeStyles = (p: Palette): Record<string, React.CSSProperties> => ({\n container: {\n padding: '12px 16px',\n fontFamily: p.fontBase,\n fontSize: 13,\n lineHeight: 1.5,\n color: p.text,\n },\n empty: {\n color: p.muted,\n margin: 0,\n },\n code: {\n fontFamily: p.fontMono,\n fontSize: '0.85em',\n background: p.mutedBg,\n borderRadius: 3,\n padding: '1px 5px',\n },\n list: {\n display: 'flex',\n flexDirection: 'column',\n gap: 2,\n margin: 0,\n padding: 0,\n listStyle: 'none',\n },\n entry: {\n padding: '8px 10px',\n borderRadius: 6,\n border: '1px solid transparent',\n cursor: 'default',\n outline: 'none',\n transition: 'background 120ms ease, border-color 120ms ease',\n },\n header: {\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n },\n indicator: {\n width: 8,\n height: 8,\n borderRadius: '50%',\n border: `2px solid ${p.border}`,\n flexShrink: 0,\n transition: 'background 120ms ease, border-color 120ms ease',\n },\n name: {\n fontWeight: 700,\n flex: 1,\n },\n id: {\n fontFamily: p.fontMono,\n fontSize: 10,\n padding: '1px 6px',\n borderRadius: 4,\n background: p.mutedBg,\n border: `1px solid ${p.border}`,\n color: p.muted,\n flexShrink: 0,\n },\n description: {\n margin: '4px 0 0',\n paddingLeft: 16,\n color: p.muted,\n },\n});\n\n/**\n * An inline `<code>` styled for the surrounding message text. Exported so\n * both runtimes can compose their own empty-state copy without duplicating\n * the style object.\n */\nexport const AnatomyCode: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const styles = makeStyles(usePalette());\n return <code style={styles.code}>{children}</code>;\n};\n\n/** A padded, muted paragraph \u2014 used for every \"nothing to show\" state. */\nexport const AnatomyMessage: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const styles = makeStyles(usePalette());\n return (\n <div style={styles.container}>\n <p style={styles.empty}>{children}</p>\n </div>\n );\n};\n\nexport type AnatomyTableProps = {\n /** Parts to list, in the order they should be shown. */\n parts: AnatomyPartDefinition[];\n /** Id of the part currently highlighted in the canvas, if any. */\n activeId?: string | null;\n /** The story's `anatomy.preset` \u2014 the active row follows it. */\n preset?: AnatomyPresetName;\n /** The story's `anatomy.theme` \u2014 token overrides on top of the preset. */\n theme?: AnatomyTheme;\n /** Called when the user hovers or focuses an entry. */\n onItemEnter?: (partId: string) => void;\n /** Called when the user leaves or blurs an entry. */\n onItemLeave?: () => void;\n};\n\nexport const AnatomyTable: React.FC<AnatomyTableProps> = ({\n parts,\n activeId = null,\n preset,\n theme,\n onItemEnter,\n onItemLeave,\n}) => {\n const palette = usePalette();\n const styles = makeStyles(palette);\n const accent = useAccent(palette, preset, theme);\n\n return (\n <div style={styles.container}>\n <ul style={styles.list} role=\"list\" aria-label=\"Anatomy parts\">\n {parts.map((part) => {\n const active = activeId === part.id;\n return (\n <li\n key={part.id}\n role=\"listitem\"\n tabIndex={0}\n aria-label={part.name}\n style={{\n ...styles.entry,\n background: active ? `color-mix(in srgb, ${accent} 7%, transparent)` : undefined,\n borderColor: active\n ? `color-mix(in srgb, ${accent} 20%, transparent)`\n : 'transparent',\n }}\n onMouseEnter={() => onItemEnter?.(part.id)}\n onMouseLeave={() => onItemLeave?.()}\n onFocus={() => onItemEnter?.(part.id)}\n onBlur={() => onItemLeave?.()}\n >\n <div style={styles.header}>\n <span\n aria-hidden=\"true\"\n style={{\n ...styles.indicator,\n background: active ? accent : undefined,\n borderColor: active ? accent : palette.border,\n }}\n />\n <span style={{ ...styles.name, color: active ? accent : undefined }}>\n {part.name}\n </span>\n <code style={styles.id}>{part.id}</code>\n </div>\n {part.description && <p style={styles.description}>{part.description}</p>}\n </li>\n );\n })}\n </ul>\n </div>\n );\n};\n"],
5
+ "mappings": "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",
6
+ "names": ["React", "AnatomyTable", "React"]
7
7
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,42 @@
1
+ import type { AnatomyParameters } from './types.js';
1
2
  export { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';
2
3
  export type { AnatomyParameters } from './types.js';
4
+ /**
5
+ * What this addon contributes to a CSF Next project's type context: a typed
6
+ * `parameters.anatomy` on every meta and story of a preview that registers it.
7
+ */
8
+ export type ComponentAnatomyTypes = {
9
+ parameters: {
10
+ /** @see {@link AnatomyParameters} */
11
+ anatomy?: AnatomyParameters;
12
+ };
13
+ };
14
+ export default _default;
15
+ /**
16
+ * The addon's preview annotations, for a CSF Next `preview.ts`:
17
+ *
18
+ * ```ts
19
+ * import { definePreview } from '@storybook/your-framework';
20
+ * import componentAnatomy from '@component-anatomy/storybook';
21
+ *
22
+ * export default definePreview({
23
+ * addons: [componentAnatomy()],
24
+ * });
25
+ * ```
26
+ *
27
+ * `.storybook/main.ts` must still list the addon — `addons:
28
+ * ['@component-anatomy/storybook']` — since that is what loads the manager
29
+ * panel. What changes under CSF Next is the preview side: a `preview.ts` built
30
+ * with `definePreview` composes *only* its own `addons`, and Storybook drops
31
+ * every addon annotation main.ts would otherwise have contributed. Without the
32
+ * call below, the canvas decorator never mounts and the panel stays empty.
33
+ *
34
+ * Registering in both places is safe — the two paths are mutually exclusive,
35
+ * so the decorator is composed once either way.
36
+ *
37
+ * The `./blocks` subpath, not this entry, holds the `<Anatomy>` MDX block: it
38
+ * needs React and `@storybook/addon-docs`, both optional peers that must not
39
+ * become load-bearing for a Storybook that only wants the panel.
40
+ */
41
+ declare function _default(): import("storybook/internal/csf").PreviewAddon<ComponentAnatomyTypes>;
3
42
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE;QACV,qCAAqC;QACrC,OAAO,CAAC,EAAE,iBAAiB,CAAC;KAC7B,CAAC;CACH,CAAC;;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG"}
package/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ // src/index.ts
2
+ import { definePreviewAddon } from "storybook/internal/csf";
3
+
4
+ // src/preview.ts
5
+ import { addons, useEffect } from "storybook/preview-api";
6
+ import { createAnatomy } from "@component-anatomy/core";
7
+
1
8
  // src/constants.ts
2
9
  var ADDON_ID = "component-anatomy";
3
10
  var PANEL_ID = `${ADDON_ID}/panel`;
@@ -16,10 +23,78 @@ var EVENTS = {
16
23
  /** consumers → preview: a panel/block mounted and wants the current part list. */
17
24
  PARTS_REQUEST: `${ADDON_ID}/parts-request`
18
25
  };
26
+
27
+ // src/channel.ts
28
+ var matchesStory = (eventStoryId, storyId) => !eventStoryId || !storyId || eventStoryId === storyId;
29
+
30
+ // src/preview.ts
31
+ var withComponentAnatomy = (storyFn, context) => {
32
+ const params = context.parameters?.[PARAM_KEY];
33
+ useEffect(() => {
34
+ if (!params || params.disable) return;
35
+ const channel = addons.getChannel();
36
+ const canvas = context.canvasElement;
37
+ if (!canvas) return;
38
+ const storyId = context.id;
39
+ const root = params.root ? canvas.querySelector(params.root) ?? canvas : canvas;
40
+ const controller = createAnatomy({
41
+ root,
42
+ parts: params.parts,
43
+ preset: params.preset,
44
+ theme: params.theme,
45
+ overlay: {
46
+ label: params.overlayLabel !== false,
47
+ padding: params.overlayPadding
48
+ }
49
+ });
50
+ const announceParts = () => channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });
51
+ announceParts();
52
+ const offEnter = controller.on(
53
+ "part:enter",
54
+ (partId) => channel.emit(EVENTS.PART_ENTER, { storyId, partId })
55
+ );
56
+ const offLeave = controller.on(
57
+ "part:leave",
58
+ () => channel.emit(EVENTS.PART_LEAVE, { storyId })
59
+ );
60
+ const onHoverItem = (event) => {
61
+ if (!matchesStory(event?.storyId, storyId)) return;
62
+ controller.highlight(event.partId);
63
+ };
64
+ const onLeaveItem = (event = {}) => {
65
+ if (!matchesStory(event?.storyId, storyId)) return;
66
+ controller.unhighlight();
67
+ };
68
+ const onPartsRequest = (event = {}) => {
69
+ if (!matchesStory(event?.storyId, storyId)) return;
70
+ announceParts();
71
+ };
72
+ channel.on(EVENTS.HOVER_ITEM, onHoverItem);
73
+ channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);
74
+ channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);
75
+ return () => {
76
+ channel.off(EVENTS.HOVER_ITEM, onHoverItem);
77
+ channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);
78
+ channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);
79
+ offEnter();
80
+ offLeave();
81
+ controller.destroy();
82
+ };
83
+ }, [context.id]);
84
+ return storyFn();
85
+ };
86
+ var annotations = {
87
+ decorators: [withComponentAnatomy]
88
+ };
89
+ var preview_default = annotations;
90
+
91
+ // src/index.ts
92
+ var index_default = () => definePreviewAddon(preview_default);
19
93
  export {
20
94
  ADDON_ID,
21
95
  EVENTS,
22
96
  PANEL_ID,
23
- PARAM_KEY
97
+ PARAM_KEY,
98
+ index_default as default
24
99
  };
25
100
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/constants.ts"],
4
- "sourcesContent": ["export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n"],
5
- "mappings": ";AAAO,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;",
3
+ "sources": ["../src/index.ts", "../src/preview.ts", "../src/constants.ts", "../src/channel.ts"],
4
+ "sourcesContent": ["import { definePreviewAddon } from 'storybook/internal/csf';\n\nimport annotations from './preview.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';\nexport type { AnatomyParameters } from './types.js';\n\n/**\n * What this addon contributes to a CSF Next project's type context: a typed\n * `parameters.anatomy` on every meta and story of a preview that registers it.\n */\nexport type ComponentAnatomyTypes = {\n parameters: {\n /** @see {@link AnatomyParameters} */\n anatomy?: AnatomyParameters;\n };\n};\n\n/**\n * The addon's preview annotations, for a CSF Next `preview.ts`:\n *\n * ```ts\n * import { definePreview } from '@storybook/your-framework';\n * import componentAnatomy from '@component-anatomy/storybook';\n *\n * export default definePreview({\n * addons: [componentAnatomy()],\n * });\n * ```\n *\n * `.storybook/main.ts` must still list the addon \u2014 `addons:\n * ['@component-anatomy/storybook']` \u2014 since that is what loads the manager\n * panel. What changes under CSF Next is the preview side: a `preview.ts` built\n * with `definePreview` composes *only* its own `addons`, and Storybook drops\n * every addon annotation main.ts would otherwise have contributed. Without the\n * call below, the canvas decorator never mounts and the panel stays empty.\n *\n * Registering in both places is safe \u2014 the two paths are mutually exclusive,\n * so the decorator is composed once either way.\n *\n * The `./blocks` subpath, not this entry, holds the `<Anatomy>` MDX block: it\n * needs React and `@storybook/addon-docs`, both optional peers that must not\n * become load-bearing for a Storybook that only wants the panel.\n */\nexport default () => definePreviewAddon<ComponentAnatomyTypes>(annotations);\n", "/**\n * Preview-side (iframe) entry. Registers a global decorator that mounts a\n * component-anatomy controller over the story canvas and syncs hover state\n * with the manager panel \u2014 and with any `<Anatomy>` doc block on the same\n * docs page \u2014 over the addon channel.\n *\n * The decorator runs in docs view too: the docs `Story` block renders each\n * story through `renderStoryToElement`, which sets `context.canvasElement`\n * exactly as it does in story view. That is what makes auto-discovery and\n * hover sync work inside MDX.\n */\nimport { addons, useEffect } from 'storybook/preview-api';\nimport type {\n ProjectAnnotations,\n Renderer,\n PartialStoryFn,\n StoryContext,\n} from 'storybook/internal/types';\nimport { createAnatomy } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { HoverItemEvent, StoryScopedEvent } from './channel.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport const withComponentAnatomy = (\n storyFn: PartialStoryFn<Renderer>,\n context: StoryContext<Renderer>\n) => {\n const params = context.parameters?.[PARAM_KEY] as AnatomyParameters | undefined;\n\n useEffect(() => {\n if (!params || params.disable) return;\n\n const channel = addons.getChannel();\n const canvas = context.canvasElement as unknown as HTMLElement;\n if (!canvas) return;\n\n const storyId = context.id;\n\n const root = params.root\n ? canvas.querySelector<HTMLElement>(params.root) ?? canvas\n : canvas;\n\n const controller = createAnatomy({\n root,\n parts: params.parts,\n preset: params.preset,\n theme: params.theme,\n overlay: {\n label: params.overlayLabel !== false,\n padding: params.overlayPadding,\n },\n });\n\n const announceParts = () =>\n channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });\n\n announceParts();\n\n const offEnter = controller.on('part:enter', (partId) =>\n channel.emit(EVENTS.PART_ENTER, { storyId, partId })\n );\n const offLeave = controller.on('part:leave', () =>\n channel.emit(EVENTS.PART_LEAVE, { storyId })\n );\n\n // A docs page mounts several stories at once, so every controller sees\n // every panel/block event \u2014 only act on the ones addressed to this story.\n const onHoverItem = (event: HoverItemEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.highlight(event.partId);\n };\n const onLeaveItem = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.unhighlight();\n };\n const onPartsRequest = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n announceParts();\n };\n\n channel.on(EVENTS.HOVER_ITEM, onHoverItem);\n channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);\n // The panel or block may mount after the story rendered \u2014 let it ask for\n // the list rather than racing the first announcement.\n channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);\n\n return () => {\n channel.off(EVENTS.HOVER_ITEM, onHoverItem);\n channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);\n channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);\n offEnter();\n offLeave();\n controller.destroy();\n };\n }, [context.id]);\n\n return storyFn();\n};\n\nexport const decorators = [withComponentAnatomy];\n\n/**\n * The same annotations as a default export, which is the shape\n * `definePreviewAddon` takes in the package's main entry (see `index.ts`) and\n * the shape a consumer gets from `@component-anatomy/storybook/preview`.\n *\n * Storybook reads `module.default[field] ?? module[field]`, so a preview\n * annotation module that exports both is read exactly once \u2014 the named\n * `decorators` above stays for anyone importing it directly.\n */\nconst annotations: ProjectAnnotations<Renderer> = {\n decorators: [withComponentAnatomy],\n};\n\nexport default annotations;\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n"],
5
+ "mappings": ";AAAA,SAAS,0BAA0B;;;ACWnC,SAAS,QAAQ,iBAAiB;AAOlC,SAAS,qBAAqB;;;AClBvB,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;;;ACKO,IAAM,eAAe,CAC1B,cACA,YACY,CAAC,gBAAgB,CAAC,WAAW,iBAAiB;;;AFTrD,IAAM,uBAAuB,CAClC,SACA,YACG;AACH,QAAM,SAAS,QAAQ,aAAa,SAAS;AAE7C,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,QAAS;AAE/B,UAAM,UAAU,OAAO,WAAW;AAClC,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,QAAQ;AAExB,UAAM,OAAO,OAAO,OAChB,OAAO,cAA2B,OAAO,IAAI,KAAK,SAClD;AAEJ,UAAM,aAAa,cAAc;AAAA,MAC/B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,QACP,OAAO,OAAO,iBAAiB;AAAA,QAC/B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AAED,UAAM,gBAAgB,MACpB,QAAQ,KAAK,OAAO,OAAO,EAAE,SAAS,OAAO,WAAW,SAAS,EAAE,CAAC;AAEtE,kBAAc;AAEd,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,CAAC,WAC5C,QAAQ,KAAK,OAAO,YAAY,EAAE,SAAS,OAAO,CAAC;AAAA,IACrD;AACA,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,MAC3C,QAAQ,KAAK,OAAO,YAAY,EAAE,QAAQ,CAAC;AAAA,IAC7C;AAIA,UAAM,cAAc,CAAC,UAA0B;AAC7C,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,UAAU,MAAM,MAAM;AAAA,IACnC;AACA,UAAM,cAAc,CAAC,QAA0B,CAAC,MAAM;AACpD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,YAAY;AAAA,IACzB;AACA,UAAM,iBAAiB,CAAC,QAA0B,CAAC,MAAM;AACvD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,oBAAc;AAAA,IAChB;AAEA,YAAQ,GAAG,OAAO,YAAY,WAAW;AACzC,YAAQ,GAAG,OAAO,YAAY,WAAW;AAGzC,YAAQ,GAAG,OAAO,eAAe,cAAc;AAE/C,WAAO,MAAM;AACX,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,eAAe,cAAc;AAChD,eAAS;AACT,eAAS;AACT,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQ,EAAE,CAAC;AAEf,SAAO,QAAQ;AACjB;AAaA,IAAM,cAA4C;AAAA,EAChD,YAAY,CAAC,oBAAoB;AACnC;AAEA,IAAO,kBAAQ;;;ADvEf,IAAO,gBAAQ,MAAM,mBAA0C,eAAW;",
6
6
  "names": []
7
7
  }
package/dist/preview.d.ts CHANGED
@@ -1,4 +1,15 @@
1
- import type { Renderer, PartialStoryFn, StoryContext } from 'storybook/internal/types';
1
+ import type { ProjectAnnotations, Renderer, PartialStoryFn, StoryContext } from 'storybook/internal/types';
2
2
  export declare const withComponentAnatomy: (storyFn: PartialStoryFn<Renderer>, context: StoryContext<Renderer>) => any;
3
3
  export declare const decorators: (typeof withComponentAnatomy)[];
4
+ /**
5
+ * The same annotations as a default export, which is the shape
6
+ * `definePreviewAddon` takes in the package's main entry (see `index.ts`) and
7
+ * the shape a consumer gets from `@component-anatomy/storybook/preview`.
8
+ *
9
+ * Storybook reads `module.default[field] ?? module[field]`, so a preview
10
+ * annotation module that exports both is read exactly once — the named
11
+ * `decorators` above stays for anyone importing it directly.
12
+ */
13
+ declare const annotations: ProjectAnnotations<Renderer>;
14
+ export default annotations;
4
15
  //# sourceMappingURL=preview.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAQvF,eAAO,MAAM,oBAAoB,YACtB,cAAc,CAAC,QAAQ,CAAC,WACxB,YAAY,CAAC,QAAQ,CAAC,QAwEhC,CAAC;AAEF,eAAO,MAAM,UAAU,iCAAyB,CAAC"}
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../src/preview.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,kBAAkB,EAClB,QAAQ,EACR,cAAc,EACd,YAAY,EACb,MAAM,0BAA0B,CAAC;AAQlC,eAAO,MAAM,oBAAoB,YACtB,cAAc,CAAC,QAAQ,CAAC,WACxB,YAAY,CAAC,QAAQ,CAAC,QAwEhC,CAAC;AAEF,eAAO,MAAM,UAAU,iCAAyB,CAAC;AAEjD;;;;;;;;GAQG;AACH,QAAA,MAAM,WAAW,EAAE,kBAAkB,CAAC,QAAQ,CAE7C,CAAC;eAEa,WAAW"}
package/dist/preview.js CHANGED
@@ -81,8 +81,13 @@ var withComponentAnatomy = (storyFn, context) => {
81
81
  return storyFn();
82
82
  };
83
83
  var decorators = [withComponentAnatomy];
84
+ var annotations = {
85
+ decorators: [withComponentAnatomy]
86
+ };
87
+ var preview_default = annotations;
84
88
  export {
85
89
  decorators,
90
+ preview_default as default,
86
91
  withComponentAnatomy
87
92
  };
88
93
  //# sourceMappingURL=preview.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/preview.ts", "../src/constants.ts", "../src/channel.ts"],
4
- "sourcesContent": ["/**\n * Preview-side (iframe) entry. Registers a global decorator that mounts a\n * component-anatomy controller over the story canvas and syncs hover state\n * with the manager panel \u2014 and with any `<Anatomy>` doc block on the same\n * docs page \u2014 over the addon channel.\n *\n * The decorator runs in docs view too: the docs `Story` block renders each\n * story through `renderStoryToElement`, which sets `context.canvasElement`\n * exactly as it does in story view. That is what makes auto-discovery and\n * hover sync work inside MDX.\n */\nimport { addons, useEffect } from 'storybook/preview-api';\nimport type { Renderer, PartialStoryFn, StoryContext } from 'storybook/internal/types';\nimport { createAnatomy } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { HoverItemEvent, StoryScopedEvent } from './channel.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport const withComponentAnatomy = (\n storyFn: PartialStoryFn<Renderer>,\n context: StoryContext<Renderer>\n) => {\n const params = context.parameters?.[PARAM_KEY] as AnatomyParameters | undefined;\n\n useEffect(() => {\n if (!params || params.disable) return;\n\n const channel = addons.getChannel();\n const canvas = context.canvasElement as unknown as HTMLElement;\n if (!canvas) return;\n\n const storyId = context.id;\n\n const root = params.root\n ? canvas.querySelector<HTMLElement>(params.root) ?? canvas\n : canvas;\n\n const controller = createAnatomy({\n root,\n parts: params.parts,\n preset: params.preset,\n theme: params.theme,\n overlay: {\n label: params.overlayLabel !== false,\n padding: params.overlayPadding,\n },\n });\n\n const announceParts = () =>\n channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });\n\n announceParts();\n\n const offEnter = controller.on('part:enter', (partId) =>\n channel.emit(EVENTS.PART_ENTER, { storyId, partId })\n );\n const offLeave = controller.on('part:leave', () =>\n channel.emit(EVENTS.PART_LEAVE, { storyId })\n );\n\n // A docs page mounts several stories at once, so every controller sees\n // every panel/block event \u2014 only act on the ones addressed to this story.\n const onHoverItem = (event: HoverItemEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.highlight(event.partId);\n };\n const onLeaveItem = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.unhighlight();\n };\n const onPartsRequest = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n announceParts();\n };\n\n channel.on(EVENTS.HOVER_ITEM, onHoverItem);\n channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);\n // The panel or block may mount after the story rendered \u2014 let it ask for\n // the list rather than racing the first announcement.\n channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);\n\n return () => {\n channel.off(EVENTS.HOVER_ITEM, onHoverItem);\n channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);\n channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);\n offEnter();\n offLeave();\n controller.destroy();\n };\n }, [context.id]);\n\n return storyFn();\n};\n\nexport const decorators = [withComponentAnatomy];\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n"],
5
- "mappings": ";AAWA,SAAS,QAAQ,iBAAiB;AAElC,SAAS,qBAAqB;;;ACbvB,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;;;ACKO,IAAM,eAAe,CAC1B,cACA,YACY,CAAC,gBAAgB,CAAC,WAAW,iBAAiB;;;AFdrD,IAAM,uBAAuB,CAClC,SACA,YACG;AACH,QAAM,SAAS,QAAQ,aAAa,SAAS;AAE7C,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,QAAS;AAE/B,UAAM,UAAU,OAAO,WAAW;AAClC,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,QAAQ;AAExB,UAAM,OAAO,OAAO,OAChB,OAAO,cAA2B,OAAO,IAAI,KAAK,SAClD;AAEJ,UAAM,aAAa,cAAc;AAAA,MAC/B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,QACP,OAAO,OAAO,iBAAiB;AAAA,QAC/B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AAED,UAAM,gBAAgB,MACpB,QAAQ,KAAK,OAAO,OAAO,EAAE,SAAS,OAAO,WAAW,SAAS,EAAE,CAAC;AAEtE,kBAAc;AAEd,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,CAAC,WAC5C,QAAQ,KAAK,OAAO,YAAY,EAAE,SAAS,OAAO,CAAC;AAAA,IACrD;AACA,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,MAC3C,QAAQ,KAAK,OAAO,YAAY,EAAE,QAAQ,CAAC;AAAA,IAC7C;AAIA,UAAM,cAAc,CAAC,UAA0B;AAC7C,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,UAAU,MAAM,MAAM;AAAA,IACnC;AACA,UAAM,cAAc,CAAC,QAA0B,CAAC,MAAM;AACpD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,YAAY;AAAA,IACzB;AACA,UAAM,iBAAiB,CAAC,QAA0B,CAAC,MAAM;AACvD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,oBAAc;AAAA,IAChB;AAEA,YAAQ,GAAG,OAAO,YAAY,WAAW;AACzC,YAAQ,GAAG,OAAO,YAAY,WAAW;AAGzC,YAAQ,GAAG,OAAO,eAAe,cAAc;AAE/C,WAAO,MAAM;AACX,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,eAAe,cAAc;AAChD,eAAS;AACT,eAAS;AACT,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQ,EAAE,CAAC;AAEf,SAAO,QAAQ;AACjB;AAEO,IAAM,aAAa,CAAC,oBAAoB;",
4
+ "sourcesContent": ["/**\n * Preview-side (iframe) entry. Registers a global decorator that mounts a\n * component-anatomy controller over the story canvas and syncs hover state\n * with the manager panel \u2014 and with any `<Anatomy>` doc block on the same\n * docs page \u2014 over the addon channel.\n *\n * The decorator runs in docs view too: the docs `Story` block renders each\n * story through `renderStoryToElement`, which sets `context.canvasElement`\n * exactly as it does in story view. That is what makes auto-discovery and\n * hover sync work inside MDX.\n */\nimport { addons, useEffect } from 'storybook/preview-api';\nimport type {\n ProjectAnnotations,\n Renderer,\n PartialStoryFn,\n StoryContext,\n} from 'storybook/internal/types';\nimport { createAnatomy } from '@component-anatomy/core';\n\nimport { EVENTS, PARAM_KEY } from './constants.js';\nimport { matchesStory } from './channel.js';\nimport type { HoverItemEvent, StoryScopedEvent } from './channel.js';\nimport type { AnatomyParameters } from './types.js';\n\nexport const withComponentAnatomy = (\n storyFn: PartialStoryFn<Renderer>,\n context: StoryContext<Renderer>\n) => {\n const params = context.parameters?.[PARAM_KEY] as AnatomyParameters | undefined;\n\n useEffect(() => {\n if (!params || params.disable) return;\n\n const channel = addons.getChannel();\n const canvas = context.canvasElement as unknown as HTMLElement;\n if (!canvas) return;\n\n const storyId = context.id;\n\n const root = params.root\n ? canvas.querySelector<HTMLElement>(params.root) ?? canvas\n : canvas;\n\n const controller = createAnatomy({\n root,\n parts: params.parts,\n preset: params.preset,\n theme: params.theme,\n overlay: {\n label: params.overlayLabel !== false,\n padding: params.overlayPadding,\n },\n });\n\n const announceParts = () =>\n channel.emit(EVENTS.PARTS, { storyId, parts: controller.getParts() });\n\n announceParts();\n\n const offEnter = controller.on('part:enter', (partId) =>\n channel.emit(EVENTS.PART_ENTER, { storyId, partId })\n );\n const offLeave = controller.on('part:leave', () =>\n channel.emit(EVENTS.PART_LEAVE, { storyId })\n );\n\n // A docs page mounts several stories at once, so every controller sees\n // every panel/block event \u2014 only act on the ones addressed to this story.\n const onHoverItem = (event: HoverItemEvent) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.highlight(event.partId);\n };\n const onLeaveItem = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n controller.unhighlight();\n };\n const onPartsRequest = (event: StoryScopedEvent = {}) => {\n if (!matchesStory(event?.storyId, storyId)) return;\n announceParts();\n };\n\n channel.on(EVENTS.HOVER_ITEM, onHoverItem);\n channel.on(EVENTS.LEAVE_ITEM, onLeaveItem);\n // The panel or block may mount after the story rendered \u2014 let it ask for\n // the list rather than racing the first announcement.\n channel.on(EVENTS.PARTS_REQUEST, onPartsRequest);\n\n return () => {\n channel.off(EVENTS.HOVER_ITEM, onHoverItem);\n channel.off(EVENTS.LEAVE_ITEM, onLeaveItem);\n channel.off(EVENTS.PARTS_REQUEST, onPartsRequest);\n offEnter();\n offLeave();\n controller.destroy();\n };\n }, [context.id]);\n\n return storyFn();\n};\n\nexport const decorators = [withComponentAnatomy];\n\n/**\n * The same annotations as a default export, which is the shape\n * `definePreviewAddon` takes in the package's main entry (see `index.ts`) and\n * the shape a consumer gets from `@component-anatomy/storybook/preview`.\n *\n * Storybook reads `module.default[field] ?? module[field]`, so a preview\n * annotation module that exports both is read exactly once \u2014 the named\n * `decorators` above stays for anyone importing it directly.\n */\nconst annotations: ProjectAnnotations<Renderer> = {\n decorators: [withComponentAnatomy],\n};\n\nexport default annotations;\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n", "/**\n * Shared channel payload contract between the preview decorator, the manager\n * panel, and the MDX doc block.\n *\n * Every payload carries the `storyId` it refers to. In story view this is\n * redundant \u2014 only one story is mounted \u2014 but a docs page mounts *many*\n * stories at once, each with its own controller, and each `<Anatomy>` block\n * must talk to exactly one of them. Without addressing, hovering a part in\n * one block highlights the matching part in every other story on the page.\n */\nimport type { AnatomyPartDefinition } from '@component-anatomy/core';\n\n/** preview \u2192 consumers: the resolved part list for one story. */\nexport type PartsEvent = { storyId?: string; parts: AnatomyPartDefinition[] };\n\n/** preview \u2192 consumers: a part became active in that story's canvas. */\nexport type PartEnterEvent = { storyId?: string; partId: string };\n\n/** consumer \u2192 preview: highlight this part in that story's canvas. */\nexport type HoverItemEvent = { storyId?: string; partId: string };\n\n/** Payload for the events that only need to name a story. */\nexport type StoryScopedEvent = { storyId?: string };\n\n/**\n * Whether an event addressed to `eventStoryId` concerns `storyId`.\n *\n * A missing id on *either* side matches everything. That keeps the protocol\n * backward compatible: a manager panel from a newer build still understands\n * an older preview bundle that emits unaddressed events, and vice versa.\n */\nexport const matchesStory = (\n eventStoryId: string | undefined,\n storyId: string | undefined\n): boolean => !eventStoryId || !storyId || eventStoryId === storyId;\n"],
5
+ "mappings": ";AAWA,SAAS,QAAQ,iBAAiB;AAOlC,SAAS,qBAAqB;;;AClBvB,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;;;ACKO,IAAM,eAAe,CAC1B,cACA,YACY,CAAC,gBAAgB,CAAC,WAAW,iBAAiB;;;AFTrD,IAAM,uBAAuB,CAClC,SACA,YACG;AACH,QAAM,SAAS,QAAQ,aAAa,SAAS;AAE7C,YAAU,MAAM;AACd,QAAI,CAAC,UAAU,OAAO,QAAS;AAE/B,UAAM,UAAU,OAAO,WAAW;AAClC,UAAM,SAAS,QAAQ;AACvB,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,QAAQ;AAExB,UAAM,OAAO,OAAO,OAChB,OAAO,cAA2B,OAAO,IAAI,KAAK,SAClD;AAEJ,UAAM,aAAa,cAAc;AAAA,MAC/B;AAAA,MACA,OAAO,OAAO;AAAA,MACd,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,SAAS;AAAA,QACP,OAAO,OAAO,iBAAiB;AAAA,QAC/B,SAAS,OAAO;AAAA,MAClB;AAAA,IACF,CAAC;AAED,UAAM,gBAAgB,MACpB,QAAQ,KAAK,OAAO,OAAO,EAAE,SAAS,OAAO,WAAW,SAAS,EAAE,CAAC;AAEtE,kBAAc;AAEd,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,CAAC,WAC5C,QAAQ,KAAK,OAAO,YAAY,EAAE,SAAS,OAAO,CAAC;AAAA,IACrD;AACA,UAAM,WAAW,WAAW;AAAA,MAAG;AAAA,MAAc,MAC3C,QAAQ,KAAK,OAAO,YAAY,EAAE,QAAQ,CAAC;AAAA,IAC7C;AAIA,UAAM,cAAc,CAAC,UAA0B;AAC7C,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,UAAU,MAAM,MAAM;AAAA,IACnC;AACA,UAAM,cAAc,CAAC,QAA0B,CAAC,MAAM;AACpD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,iBAAW,YAAY;AAAA,IACzB;AACA,UAAM,iBAAiB,CAAC,QAA0B,CAAC,MAAM;AACvD,UAAI,CAAC,aAAa,OAAO,SAAS,OAAO,EAAG;AAC5C,oBAAc;AAAA,IAChB;AAEA,YAAQ,GAAG,OAAO,YAAY,WAAW;AACzC,YAAQ,GAAG,OAAO,YAAY,WAAW;AAGzC,YAAQ,GAAG,OAAO,eAAe,cAAc;AAE/C,WAAO,MAAM;AACX,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,YAAY,WAAW;AAC1C,cAAQ,IAAI,OAAO,eAAe,cAAc;AAChD,eAAS;AACT,eAAS;AACT,iBAAW,QAAQ;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,QAAQ,EAAE,CAAC;AAEf,SAAO,QAAQ;AACjB;AAEO,IAAM,aAAa,CAAC,oBAAoB;AAW/C,IAAM,cAA4C;AAAA,EAChD,YAAY,CAAC,oBAAoB;AACnC;AAEA,IAAO,kBAAQ;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@component-anatomy/storybook",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Storybook addon — interactive component anatomy panel synced with the story canvas",
5
5
  "license": "MIT",
6
6
  "author": "Julien Déramond",
@@ -37,18 +37,23 @@
37
37
  "exports": {
38
38
  ".": {
39
39
  "types": "./dist/index.d.ts",
40
- "import": "./dist/index.js",
41
- "require": "./dist/index.cjs"
40
+ "default": "./dist/index.js"
42
41
  },
43
42
  "./blocks": {
44
43
  "types": "./dist/blocks.d.ts",
45
- "import": "./dist/blocks.js"
44
+ "default": "./dist/blocks.js"
45
+ },
46
+ "./manager": {
47
+ "types": "./dist/manager.d.ts",
48
+ "default": "./dist/manager.js"
49
+ },
50
+ "./preview": {
51
+ "types": "./dist/preview.d.ts",
52
+ "default": "./dist/preview.js"
46
53
  },
47
- "./manager": "./dist/manager.js",
48
- "./preview": "./dist/preview.js",
49
54
  "./package.json": "./package.json"
50
55
  },
51
- "main": "./dist/index.cjs",
56
+ "main": "./dist/index.js",
52
57
  "module": "./dist/index.js",
53
58
  "types": "./dist/index.d.ts",
54
59
  "files": [
@@ -63,18 +68,18 @@
63
68
  "@component-anatomy/core": "^0.1.0"
64
69
  },
65
70
  "devDependencies": {
66
- "@storybook/addon-docs": "^10.5.2",
71
+ "@storybook/addon-docs": "11.0.0-alpha.0",
67
72
  "@types/react": "^19.2.18",
68
73
  "esbuild": "^0.28.2",
69
74
  "react": "^19.2.8",
70
75
  "react-dom": "^19.2.8",
71
- "storybook": "^10.5.10",
76
+ "storybook": "11.0.0-alpha.0",
72
77
  "typescript": "^7.0.2"
73
78
  },
74
79
  "peerDependencies": {
75
- "@storybook/addon-docs": ">=9.0.0",
80
+ "@storybook/addon-docs": "^10.0.0 || ^11.0.0-0",
76
81
  "react": ">=18",
77
- "storybook": ">=9.0.0"
82
+ "storybook": "^10.0.0 || ^11.0.0-0"
78
83
  },
79
84
  "peerDependenciesMeta": {
80
85
  "@storybook/addon-docs": {
@@ -90,6 +95,6 @@
90
95
  "scripts": {
91
96
  "build": "node build.mjs",
92
97
  "typecheck": "tsc --noEmit",
93
- "test": "node build.mjs && node ../../scripts/typecheck-fixture.mjs test/consumer-types.ts && node ../../scripts/typecheck-fixture.mjs test/consumer-blocks-types.tsx"
98
+ "test": "node build.mjs && node ../../scripts/typecheck-fixture.mjs test/consumer-types.ts && node ../../scripts/typecheck-fixture.mjs test/consumer-blocks-types.tsx && node ../../scripts/typecheck-fixture.mjs test/consumer-csf-next-types.ts"
94
99
  }
95
100
  }
package/src/blocks.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  /**
2
- * Docs blocks entry — the `<Anatomy>` block for MDX pages.
2
+ * Docs blocks entry — the `<Anatomy>` and `<AnatomyTable>` blocks for MDX
3
+ * pages.
3
4
  *
4
5
  * Unlike the addon panel, this runs in the **preview iframe**, where
5
6
  * `storybook/manager-api` does not exist. It reaches the story's controller
@@ -8,30 +9,46 @@
8
9
  * mounted the story talk to each other directly, in-frame, with no extra
9
10
  * plumbing.
10
11
  *
12
+ * `<Anatomy>` is the batteries-included block — canvas and table together,
13
+ * laid out for that pairing specifically ([#13](https://github.com/julien-deramond/component-anatomy/issues/13#issuecomment-5475936860)):
14
+ *
11
15
  * ```mdx
12
- * import { Meta, Canvas } from '@storybook/addon-docs/blocks';
16
+ * import { Meta } from '@storybook/addon-docs/blocks';
13
17
  * import { Anatomy } from '@component-anatomy/storybook/blocks';
14
18
  * import * as ButtonStories from './Button.stories';
15
19
  *
16
20
  * <Meta of={ButtonStories} />
17
21
  *
18
- * <Canvas of={ButtonStories.Anatomy} />
19
22
  * <Anatomy of={ButtonStories.Anatomy} />
20
23
  * ```
24
+ *
25
+ * `<AnatomyTable>` is the table alone, for a hand-placed `<Canvas>` or a
26
+ * custom layout:
27
+ *
28
+ * ```mdx
29
+ * import { Canvas, Meta } from '@storybook/addon-docs/blocks';
30
+ * import { AnatomyTable } from '@component-anatomy/storybook/blocks';
31
+ * import * as ButtonStories from './Button.stories';
32
+ *
33
+ * <Meta of={ButtonStories} />
34
+ *
35
+ * <Canvas of={ButtonStories.Anatomy} />
36
+ * <AnatomyTable of={ButtonStories.Anatomy} />
37
+ * ```
21
38
  */
22
39
  import React, { useEffect, useState } from 'react';
23
40
  import { addons } from 'storybook/preview-api';
24
- import { Unstyled, useOf } from '@storybook/addon-docs/blocks';
41
+ import { Canvas, Unstyled, useOf } from '@storybook/addon-docs/blocks';
25
42
  import type { Of } from '@storybook/addon-docs/blocks';
26
43
  import type { AnatomyPartDefinition } from '@component-anatomy/core';
27
44
 
28
45
  import { EVENTS, PARAM_KEY } from './constants.js';
29
46
  import { matchesStory } from './channel.js';
30
47
  import type { PartEnterEvent, PartsEvent, StoryScopedEvent } from './channel.js';
31
- import { AnatomyCode, AnatomyMessage, AnatomyTable } from './AnatomyTable.js';
48
+ import { AnatomyCode, AnatomyMessage, AnatomyTable as AnatomyTablePrimitive } from './AnatomyTable.js';
32
49
  import type { AnatomyParameters } from './types.js';
33
50
 
34
- export type AnatomyBlockProps = {
51
+ export type AnatomyTableProps = {
35
52
  /**
36
53
  * The CSF export to document — a story export, or the whole module export
37
54
  * of a CSF file to read the meta's parameters.
@@ -68,7 +85,7 @@ function getChannelSafely() {
68
85
  }
69
86
  }
70
87
 
71
- export const Anatomy: React.FC<AnatomyBlockProps> = ({ of, parts: partsProp, sync = true }) => {
88
+ export const AnatomyTable: React.FC<AnatomyTableProps> = ({ of, parts: partsProp, sync = true }) => {
72
89
  const resolved = useOf(of ?? 'story', ['story', 'meta']);
73
90
 
74
91
  const params = (
@@ -183,7 +200,7 @@ export const Anatomy: React.FC<AnatomyBlockProps> = ({ of, parts: partsProp, syn
183
200
 
184
201
  return (
185
202
  <Unstyled>
186
- <AnatomyTable
203
+ <AnatomyTablePrimitive
187
204
  parts={parts}
188
205
  activeId={activeId}
189
206
  preset={params?.preset}
@@ -194,3 +211,57 @@ export const Anatomy: React.FC<AnatomyBlockProps> = ({ of, parts: partsProp, syn
194
211
  </Unstyled>
195
212
  );
196
213
  };
214
+
215
+ /**
216
+ * Injects the rule that tightens `<Anatomy>`'s canvas-to-table gap, once per
217
+ * page — a module-level flag rather than `useState`, so mounting a second
218
+ * `<Anatomy>` block doesn't append the rule again. Scoped under
219
+ * `.ca-anatomy` so it never touches a bare `<Canvas>` placed by the consumer.
220
+ *
221
+ * Self-injected rather than asked of the consumer's `preview-head.html`: the
222
+ * block owns both elements it is bridging, so the gap between them is its
223
+ * concern, not config the consumer should have to wire up.
224
+ */
225
+ let canvasGapStyleInjected = false;
226
+ function useCanvasGapStyle() {
227
+ useEffect(() => {
228
+ if (canvasGapStyleInjected || typeof document === 'undefined') return;
229
+ canvasGapStyleInjected = true;
230
+ const style = document.createElement('style');
231
+ style.dataset.componentAnatomy = 'anatomy-block';
232
+ style.textContent = '.ca-anatomy .sbdocs-preview { margin-bottom: 8px; }';
233
+ document.head.appendChild(style);
234
+ }, []);
235
+ }
236
+
237
+ export type AnatomyProps = AnatomyTableProps & {
238
+ /**
239
+ * Passed straight to the underlying `<Canvas>`. `'none'` (the default)
240
+ * drops the show-code button along with the panel it opens — chrome that
241
+ * mostly restates what the anatomy table already documents. Set explicitly
242
+ * to keep it, e.g. for a consumer relying on that source elsewhere.
243
+ */
244
+ sourceState?: 'hidden' | 'shown' | 'none';
245
+ };
246
+
247
+ /**
248
+ * The canvas and its anatomy table, paired and tightened for that specific
249
+ * combination ([#13](https://github.com/julien-deramond/component-anatomy/issues/13#issuecomment-5475936860)).
250
+ * For a hand-placed `<Canvas>` — a custom layout, or a source panel this
251
+ * block would otherwise hide — use `<AnatomyTable>` next to your own
252
+ * `<Canvas>` instead.
253
+ */
254
+ export const Anatomy: React.FC<AnatomyProps> = ({
255
+ of,
256
+ parts,
257
+ sync,
258
+ sourceState = 'none',
259
+ }) => {
260
+ useCanvasGapStyle();
261
+ return (
262
+ <div className="ca-anatomy">
263
+ <Canvas of={of} sourceState={sourceState} />
264
+ <AnatomyTable of={of} parts={parts} sync={sync} />
265
+ </div>
266
+ );
267
+ };
package/src/index.ts CHANGED
@@ -1,7 +1,46 @@
1
+ import { definePreviewAddon } from 'storybook/internal/csf';
2
+
3
+ import annotations from './preview.js';
4
+ import type { AnatomyParameters } from './types.js';
5
+
1
6
  export { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';
2
7
  export type { AnatomyParameters } from './types.js';
3
8
 
4
- // The `<Anatomy>` doc block lives in the `./blocks` subpath, not here: this
5
- // entry is loaded at config time by `.storybook/main.ts` (and built to CJS),
6
- // while the block needs React and `@storybook/addon-docs`, both optional
7
- // peers that must not become load-bearing for `addons: ['...']` to work.
9
+ /**
10
+ * What this addon contributes to a CSF Next project's type context: a typed
11
+ * `parameters.anatomy` on every meta and story of a preview that registers it.
12
+ */
13
+ export type ComponentAnatomyTypes = {
14
+ parameters: {
15
+ /** @see {@link AnatomyParameters} */
16
+ anatomy?: AnatomyParameters;
17
+ };
18
+ };
19
+
20
+ /**
21
+ * The addon's preview annotations, for a CSF Next `preview.ts`:
22
+ *
23
+ * ```ts
24
+ * import { definePreview } from '@storybook/your-framework';
25
+ * import componentAnatomy from '@component-anatomy/storybook';
26
+ *
27
+ * export default definePreview({
28
+ * addons: [componentAnatomy()],
29
+ * });
30
+ * ```
31
+ *
32
+ * `.storybook/main.ts` must still list the addon — `addons:
33
+ * ['@component-anatomy/storybook']` — since that is what loads the manager
34
+ * panel. What changes under CSF Next is the preview side: a `preview.ts` built
35
+ * with `definePreview` composes *only* its own `addons`, and Storybook drops
36
+ * every addon annotation main.ts would otherwise have contributed. Without the
37
+ * call below, the canvas decorator never mounts and the panel stays empty.
38
+ *
39
+ * Registering in both places is safe — the two paths are mutually exclusive,
40
+ * so the decorator is composed once either way.
41
+ *
42
+ * The `./blocks` subpath, not this entry, holds the `<Anatomy>` MDX block: it
43
+ * needs React and `@storybook/addon-docs`, both optional peers that must not
44
+ * become load-bearing for a Storybook that only wants the panel.
45
+ */
46
+ export default () => definePreviewAddon<ComponentAnatomyTypes>(annotations);
package/src/preview.ts CHANGED
@@ -10,7 +10,12 @@
10
10
  * hover sync work inside MDX.
11
11
  */
12
12
  import { addons, useEffect } from 'storybook/preview-api';
13
- import type { Renderer, PartialStoryFn, StoryContext } from 'storybook/internal/types';
13
+ import type {
14
+ ProjectAnnotations,
15
+ Renderer,
16
+ PartialStoryFn,
17
+ StoryContext,
18
+ } from 'storybook/internal/types';
14
19
  import { createAnatomy } from '@component-anatomy/core';
15
20
 
16
21
  import { EVENTS, PARAM_KEY } from './constants.js';
@@ -95,3 +100,18 @@ export const withComponentAnatomy = (
95
100
  };
96
101
 
97
102
  export const decorators = [withComponentAnatomy];
103
+
104
+ /**
105
+ * The same annotations as a default export, which is the shape
106
+ * `definePreviewAddon` takes in the package's main entry (see `index.ts`) and
107
+ * the shape a consumer gets from `@component-anatomy/storybook/preview`.
108
+ *
109
+ * Storybook reads `module.default[field] ?? module[field]`, so a preview
110
+ * annotation module that exports both is read exactly once — the named
111
+ * `decorators` above stays for anyone importing it directly.
112
+ */
113
+ const annotations: ProjectAnnotations<Renderer> = {
114
+ decorators: [withComponentAnatomy],
115
+ };
116
+
117
+ export default annotations;
package/dist/index.cjs DELETED
@@ -1,48 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/index.ts
21
- var index_exports = {};
22
- __export(index_exports, {
23
- ADDON_ID: () => ADDON_ID,
24
- EVENTS: () => EVENTS,
25
- PANEL_ID: () => PANEL_ID,
26
- PARAM_KEY: () => PARAM_KEY
27
- });
28
- module.exports = __toCommonJS(index_exports);
29
-
30
- // src/constants.ts
31
- var ADDON_ID = "component-anatomy";
32
- var PANEL_ID = `${ADDON_ID}/panel`;
33
- var PARAM_KEY = "anatomy";
34
- var EVENTS = {
35
- /** preview → consumers: a part became active in the canvas (hover/programmatic). */
36
- PART_ENTER: `${ADDON_ID}/part-enter`,
37
- /** preview → consumers: no part is active anymore. */
38
- PART_LEAVE: `${ADDON_ID}/part-leave`,
39
- /** preview → consumers: resolved part list for a story. */
40
- PARTS: `${ADDON_ID}/parts`,
41
- /** consumers → preview: the user hovers/focuses a panel entry. */
42
- HOVER_ITEM: `${ADDON_ID}/hover-item`,
43
- /** consumers → preview: the user left a panel entry. */
44
- LEAVE_ITEM: `${ADDON_ID}/leave-item`,
45
- /** consumers → preview: a panel/block mounted and wants the current part list. */
46
- PARTS_REQUEST: `${ADDON_ID}/parts-request`
47
- };
48
- //# sourceMappingURL=index.cjs.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/index.ts", "../src/constants.ts"],
4
- "sourcesContent": ["export { ADDON_ID, PANEL_ID, PARAM_KEY, EVENTS } from './constants.js';\nexport type { AnatomyParameters } from './types.js';\n\n// The `<Anatomy>` doc block lives in the `./blocks` subpath, not here: this\n// entry is loaded at config time by `.storybook/main.ts` (and built to CJS),\n// while the block needs React and `@storybook/addon-docs`, both optional\n// peers that must not become load-bearing for `addons: ['...']` to work.\n", "export const ADDON_ID = 'component-anatomy';\nexport const PANEL_ID = `${ADDON_ID}/panel`;\n\n/** Story parameter key: `parameters.anatomy = { ... }` */\nexport const PARAM_KEY = 'anatomy';\n\n/**\n * Channel events used to sync the manager panel \u2014 and the `<Anatomy>` MDX doc\n * block, which runs in the preview iframe \u2014 with the story canvas.\n *\n * Every payload carries the `storyId` it concerns; see `channel.ts` for the\n * payload types and the `matchesStory` filter each listener applies.\n */\nexport const EVENTS = {\n /** preview \u2192 consumers: a part became active in the canvas (hover/programmatic). */\n PART_ENTER: `${ADDON_ID}/part-enter`,\n /** preview \u2192 consumers: no part is active anymore. */\n PART_LEAVE: `${ADDON_ID}/part-leave`,\n /** preview \u2192 consumers: resolved part list for a story. */\n PARTS: `${ADDON_ID}/parts`,\n /** consumers \u2192 preview: the user hovers/focuses a panel entry. */\n HOVER_ITEM: `${ADDON_ID}/hover-item`,\n /** consumers \u2192 preview: the user left a panel entry. */\n LEAVE_ITEM: `${ADDON_ID}/leave-item`,\n /** consumers \u2192 preview: a panel/block mounted and wants the current part list. */\n PARTS_REQUEST: `${ADDON_ID}/parts-request`,\n} as const;\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,WAAW;AACjB,IAAM,WAAW,GAAG,QAAQ;AAG5B,IAAM,YAAY;AASlB,IAAM,SAAS;AAAA;AAAA,EAEpB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,OAAO,GAAG,QAAQ;AAAA;AAAA,EAElB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,YAAY,GAAG,QAAQ;AAAA;AAAA,EAEvB,eAAe,GAAG,QAAQ;AAC5B;",
6
- "names": []
7
- }