{"_id":"@dugyu/luna-studio","_rev":"6-c4c3a6dcc2d521de2d98a3f666dad7db","name":"@dugyu/luna-studio","dist-tags":{"oidc-bootstrap":"0.0.0-oidc-bootstrap.0","latest":"0.2.0"},"versions":{"0.0.0-oidc-bootstrap.0":{"name":"@dugyu/luna-studio","version":"0.0.0-oidc-bootstrap.0","author":{"name":"The Lynx Authors"},"license":"Apache-2.0","_id":"@dugyu/luna-studio@0.0.0-oidc-bootstrap.0","maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"dist":{"shasum":"71d35cbfe9a10e674a219d842d89a06a230ad572","tarball":"https://registry.npmjs.org/@dugyu/luna-studio/-/luna-studio-0.0.0-oidc-bootstrap.0.tgz","fileCount":3,"integrity":"sha512-sxhOcxINqzY1h0x3/fWUFdV+FCAV4KspKzn/7m99FmaHwgnJKod6Rf+pCxlDlE1FoNcOuONiNXpg4pKX4WTcYA==","signatures":[{"sig":"MEYCIQCt9W+2ynfLYH95PZsREQQKG9VgHvwVkq+3cF+urElrNgIhAPy1X/X4Rke/mW6BQ78jiC9za3zRwdNON9M4Ozl+gTfQ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11993},"gitHead":"4d68012e56c46131e66d50a8716086dd378f8a5b","_npmUser":{"name":"sevensever","email":"dugy.guangyu@gmail.com"},"_npmVersion":"11.11.0","description":"Reusable multi-stage choreography layer for the L.U.N.A project (OIDC bootstrap placeholder)","directories":{},"_nodeVersion":"24.14.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/luna-studio_0.0.0-oidc-bootstrap.0_1778075869647_0.6565322351238998","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@dugyu/luna-studio","version":"0.1.0","keywords":["luna","studio","preview","choreography","react","lynx"],"author":{"name":"The Lynx Authors"},"license":"Apache-2.0","_id":"@dugyu/luna-studio@0.1.0","maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"homepage":"https://github.com/Dugyu/lunarium/tree/main/packages/luna-studio#readme","bugs":{"url":"https://github.com/Dugyu/lunarium/issues"},"dist":{"shasum":"62e1592deb23b7d1b01d18df68ede015bcc482a5","tarball":"https://registry.npmjs.org/@dugyu/luna-studio/-/luna-studio-0.1.0.tgz","fileCount":46,"integrity":"sha512-0xqA0Q0F4c5x25p+TZhYsKKfeyJCcJtWMUR1qmfSQXB5hzIRzQYYsq0E3xY+DzWvyAg8aetOGd236MBQSVmkOg==","signatures":[{"sig":"MEYCIQCgK25O9MaKyrwER+2rQfDk8mIWA24cbJtwxF3aSFY2WwIhAIwZoR7Uh1uOzyB2POWQEEPsvbvbPiu+u81YIJZnQYnj","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dugyu%2fluna-studio@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":98475},"main":"./dist/index.js","type":"module","_from":"file:dugyu-luna-studio-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"rslib build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b0bc2feb-5aab-454b-abb7-56a69e3b899d"}},"_resolved":"/tmp/2d900bff8bff04102574af30c0aac3f5/dugyu-luna-studio-0.1.0.tgz","_integrity":"sha512-0xqA0Q0F4c5x25p+TZhYsKKfeyJCcJtWMUR1qmfSQXB5hzIRzQYYsq0E3xY+DzWvyAg8aetOGd236MBQSVmkOg==","repository":{"url":"git+https://github.com/Dugyu/lunarium.git","type":"git","directory":"packages/luna-studio"},"_npmVersion":"11.11.0","description":"Reusable multi-stage choreography layer for the L.U.N.A project","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{"@dugyu/luna-core":"0.4.0","@dugyu/luna-stage":"0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"18.3.1","motion":"12.38.0","react-dom":"18.3.1","typescript":"5.9.2","@rslib/core":"0.21.1","@types/react":"18.3.24","@lynx-js/web-core":"0.20.3","@rsbuild/plugin-react":"1.4.6"},"peerDependencies":{"react":">=18.0.0","motion":">=12.23.16","react-dom":">=18.0.0","@lynx-js/web-core":">=0.20.0"},"_npmOperationalInternal":{"tmp":"tmp/luna-studio_0.1.0_1778078610802_0.974379770936588","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@dugyu/luna-studio","version":"0.1.1","keywords":["luna","studio","preview","choreography","react","lynx"],"author":{"name":"The Lynx Authors"},"license":"Apache-2.0","_id":"@dugyu/luna-studio@0.1.1","maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"homepage":"https://github.com/Dugyu/lunarium/tree/main/packages/luna-studio#readme","bugs":{"url":"https://github.com/Dugyu/lunarium/issues"},"dist":{"shasum":"12f01b4919bcea84c220e989915945bd17ec8a7c","tarball":"https://registry.npmjs.org/@dugyu/luna-studio/-/luna-studio-0.1.1.tgz","fileCount":46,"integrity":"sha512-yXSP06DYL1fnYm8vwGFhFgMdBsSTuCDIdvUPhRVHjCjixY29Lm8Ld+Ou54HGGgb5umDYwNW8Q5iOU/bJmUvx1w==","signatures":[{"sig":"MEYCIQD6fmkSmWJNqtmPFriNkgZm0+q6IBV3/MOlpx33kqOU/wIhALHZlbx/sDr3PRpxWJ+ZxSIlfG912aYchBhGX2xRjzzC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dugyu%2fluna-studio@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":98747},"main":"./dist/index.js","type":"module","_from":"file:dugyu-luna-studio-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"rslib build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b0bc2feb-5aab-454b-abb7-56a69e3b899d"}},"_resolved":"/tmp/8dfb9bdb7b7b739fdbdb19e6c620a2d5/dugyu-luna-studio-0.1.1.tgz","_integrity":"sha512-yXSP06DYL1fnYm8vwGFhFgMdBsSTuCDIdvUPhRVHjCjixY29Lm8Ld+Ou54HGGgb5umDYwNW8Q5iOU/bJmUvx1w==","repository":{"url":"git+https://github.com/Dugyu/lunarium.git","type":"git","directory":"packages/luna-studio"},"_npmVersion":"11.11.0","description":"Reusable multi-stage choreography layer for the L.U.N.A project","directories":{},"sideEffects":false,"_nodeVersion":"24.14.1","dependencies":{"@dugyu/luna-core":"0.4.0","@dugyu/luna-stage":"0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"18.3.1","motion":"12.38.0","react-dom":"18.3.1","typescript":"5.9.2","@rslib/core":"0.21.1","@types/react":"18.3.24","@lynx-js/web-core":"0.20.3","@rsbuild/plugin-react":"1.4.6"},"peerDependencies":{"react":">=18.0.0","motion":">=12.23.16","@lynx-js/web-core":">=0.20.0"},"_npmOperationalInternal":{"tmp":"tmp/luna-studio_0.1.1_1778149098198_0.6640155397280603","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@dugyu/luna-studio","version":"0.1.2","keywords":["luna","studio","preview","choreography","react","lynx"],"author":{"name":"The Lynx Authors"},"license":"Apache-2.0","_id":"@dugyu/luna-studio@0.1.2","maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"homepage":"https://github.com/Dugyu/lunarium/tree/main/packages/luna-studio#readme","bugs":{"url":"https://github.com/Dugyu/lunarium/issues"},"dist":{"shasum":"3dacc2e28e4ab4807f8695ebe526608847153291","tarball":"https://registry.npmjs.org/@dugyu/luna-studio/-/luna-studio-0.1.2.tgz","fileCount":46,"integrity":"sha512-s0Xa+ExqE5LV9h94QHqi8gVhDpPi431tuxA2rluAqBKCPsKgVuRJ9yiBydS0oN72T2O+v98rBLp8cQ/MuRa4cw==","signatures":[{"sig":"MEQCIEwfTCJz23zdDYXCYeX0n+V+c46LyIsSMdGsxjkmTNcGAiALKARuM2kvobWpH4JeK97fQgR3u9UMJrp0XlkiaZfRzA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dugyu%2fluna-studio@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":98926},"main":"./dist/index.js","type":"module","_from":"file:dugyu-luna-studio-0.1.2.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"rslib build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b0bc2feb-5aab-454b-abb7-56a69e3b899d"}},"_resolved":"/tmp/874b6136c97a77e3b1415397cb7bff2c/dugyu-luna-studio-0.1.2.tgz","_integrity":"sha512-s0Xa+ExqE5LV9h94QHqi8gVhDpPi431tuxA2rluAqBKCPsKgVuRJ9yiBydS0oN72T2O+v98rBLp8cQ/MuRa4cw==","repository":{"url":"git+https://github.com/Dugyu/lunarium.git","type":"git","directory":"packages/luna-studio"},"_npmVersion":"11.12.1","description":"Reusable multi-stage choreography layer for the L.U.N.A project","directories":{},"sideEffects":false,"_nodeVersion":"24.15.0","dependencies":{"@dugyu/luna-core":"0.4.0","@dugyu/luna-stage":"0.2.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"18.3.1","motion":"12.38.0","react-dom":"18.3.1","typescript":"5.9.2","@rslib/core":"0.21.1","@types/react":"18.3.24","@lynx-js/web-core":"0.20.3","@rsbuild/plugin-react":"1.4.6"},"peerDependencies":{"react":">=18.0.0","motion":">=12.23.16","@lynx-js/web-core":">=0.20.0"},"_npmOperationalInternal":{"tmp":"tmp/luna-studio_0.1.2_1778868070176_0.3102109154101147","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@dugyu/luna-studio","version":"0.1.3","keywords":["luna","studio","preview","choreography","react","lynx"],"author":{"name":"The Lynx Authors"},"license":"Apache-2.0","_id":"@dugyu/luna-studio@0.1.3","maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"homepage":"https://github.com/Dugyu/lunarium/tree/main/packages/luna-studio#readme","bugs":{"url":"https://github.com/Dugyu/lunarium/issues"},"dist":{"shasum":"887d068bf05ab39f39cc841f295c1f6aeef8796f","tarball":"https://registry.npmjs.org/@dugyu/luna-studio/-/luna-studio-0.1.3.tgz","fileCount":46,"integrity":"sha512-iUBNeGk4DRPaoBSQrPSTnuIch1/MkR5Fx/WBXwd/wVybgkudzSAmgZ9XjcJYqwUhgRNwIurQqM/ErlTTfqAGoQ==","signatures":[{"sig":"MEUCIDq5NGOfmcIm5v6QUqrXWbAXItuESsLwXIpaErkeW7tZAiEAsZiK7oNCcZEBghwtQWBfVeaa2C1alywsfRVqWTSkWKo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dugyu%2fluna-studio@0.1.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":99105},"main":"./dist/index.js","type":"module","_from":"file:dugyu-luna-studio-0.1.3.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"rslib build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b0bc2feb-5aab-454b-abb7-56a69e3b899d"}},"_resolved":"/tmp/1a14052ec7e3f622b3b191102f9c431c/dugyu-luna-studio-0.1.3.tgz","_integrity":"sha512-iUBNeGk4DRPaoBSQrPSTnuIch1/MkR5Fx/WBXwd/wVybgkudzSAmgZ9XjcJYqwUhgRNwIurQqM/ErlTTfqAGoQ==","repository":{"url":"git+https://github.com/Dugyu/lunarium.git","type":"git","directory":"packages/luna-studio"},"_npmVersion":"11.12.1","description":"Reusable multi-stage choreography layer for the L.U.N.A project","directories":{},"sideEffects":false,"_nodeVersion":"24.15.0","dependencies":{"@dugyu/luna-core":"0.4.0","@dugyu/luna-stage":"0.2.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"react":"18.3.1","motion":"12.38.0","react-dom":"18.3.1","typescript":"5.9.2","@rslib/core":"0.21.1","@types/react":"18.3.24","@lynx-js/web-core":"0.20.3","@rsbuild/plugin-react":"1.4.6"},"peerDependencies":{"react":">=18.0.0","motion":">=12.23.16","@lynx-js/web-core":">=0.20.0"},"_npmOperationalInternal":{"tmp":"tmp/luna-studio_0.1.3_1778923109164_0.13762323283810907","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@dugyu/luna-studio","version":"0.2.0","description":"Reusable multi-stage choreography layer for the L.U.N.A project","keywords":["luna","studio","preview","choreography","react","lynx"],"homepage":"https://github.com/Dugyu/lunarium/tree/main/packages/luna-studio#readme","repository":{"type":"git","url":"git+https://github.com/Dugyu/lunarium.git","directory":"packages/luna-studio"},"license":"Apache-2.0","author":{"name":"The Lynx Authors"},"sideEffects":false,"type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","dependencies":{"@dugyu/luna-core":"0.5.0","@dugyu/luna-stage":"0.2.2"},"devDependencies":{"@lynx-js/web-core":"0.20.3","@rsbuild/plugin-react":"1.4.6","@rslib/core":"0.21.1","@types/react":"18.3.24","motion":"12.38.0","react":"18.3.1","react-dom":"18.3.1","typescript":"5.9.2"},"peerDependencies":{"@lynx-js/web-core":">=0.20.0","motion":">=12.23.16","react":">=18.0.0"},"publishConfig":{"access":"public"},"scripts":{"build":"rslib build"},"_id":"@dugyu/luna-studio@0.2.0","bugs":{"url":"https://github.com/Dugyu/lunarium/issues"},"_integrity":"sha512-yEtmy7WGiTC3lqR1YDESyWFhC5dMi4nLcKbYegnvEw5e7RRdkaU4tcD6tB5NAKw867E1RBd6Diqv7Ib20U/xxA==","_resolved":"/tmp/da0d120d08bfa08cf26bdfeb3fb675a8/dugyu-luna-studio-0.2.0.tgz","_from":"file:dugyu-luna-studio-0.2.0.tgz","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-yEtmy7WGiTC3lqR1YDESyWFhC5dMi4nLcKbYegnvEw5e7RRdkaU4tcD6tB5NAKw867E1RBd6Diqv7Ib20U/xxA==","shasum":"32882e47079cb84a50952d48c87878f8ac1bb9ed","tarball":"https://registry.npmjs.org/@dugyu/luna-studio/-/luna-studio-0.2.0.tgz","fileCount":46,"unpackedSize":103124,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@dugyu%2fluna-studio@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHyk66kdwSIfdXQp6igmv0rS4pvZtnIeepIgz7QVkz4/AiBqgJFIb25feqgIIoWDfGyjkkANahcEjNMYr8YSJKK3/g=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b0bc2feb-5aab-454b-abb7-56a69e3b899d"}},"directories":{},"maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/luna-studio_0.2.0_1779032388882_0.6729388807662551"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-06T13:57:49.565Z","modified":"2026-05-17T15:39:49.336Z","0.0.0-oidc-bootstrap.0":"2026-05-06T13:57:49.854Z","0.1.0":"2026-05-06T14:43:30.945Z","0.1.1":"2026-05-07T10:18:18.337Z","0.1.2":"2026-05-15T18:01:10.343Z","0.1.3":"2026-05-16T09:18:29.317Z","0.2.0":"2026-05-17T15:39:49.027Z"},"bugs":{"url":"https://github.com/Dugyu/lunarium/issues"},"author":{"name":"The Lynx Authors"},"license":"Apache-2.0","homepage":"https://github.com/Dugyu/lunarium/tree/main/packages/luna-studio#readme","keywords":["luna","studio","preview","choreography","react","lynx"],"repository":{"type":"git","url":"git+https://github.com/Dugyu/lunarium.git","directory":"packages/luna-studio"},"description":"Reusable multi-stage choreography layer for the L.U.N.A project","maintainers":[{"name":"sevensever","email":"dugy.guangyu@gmail.com"}],"readme":"# luna-studio\n\n`@dugyu/luna-studio` is a reusable multi-stage choreography layer for Lynx-based previews and comparison canvases.\n\nIt packages the rendering, layout, focus, and interaction plumbing needed for:\n\n- compare / focus / lineup stage arrangements\n- shared or per-stage theme application\n- host-side stage-container interaction handling\n- embedded Lynx runtime callback forwarding\n- app-defined focus resolution and global-props injection\n\nIt does **not** ship an application shell.\n\nThe following app-level pieces stay outside this package:\n\n- `Studio`\n- `MenuBar`\n- `StudioFrame`\n- keyboard shortcut glue\n- demo-specific runtime handling\n- app-owned layout catalogs and business metadata\n\nIf you only need a single device-framed preview, use [`@dugyu/luna-stage`](../luna-stage) directly.\n\n## Installation\n\n```sh\nnpm i @dugyu/luna-studio @lynx-js/web-core motion react\n```\n\n`luna-studio` depends on `@dugyu/luna-stage` internally for its rendering primitives and expects the Lynx Web runtime to be available through `@lynx-js/web-core`.\n\n## Public API\n\nCurrent public exports from `@dugyu/luna-studio` are grouped by components and types:\n\n- Components\n  - `Choreography`\n- Studio model types\n  - `StudioStage`\n  - `StudioStageSlice`\n  - `StudioResolvedStage`\n  - `ResolveStudioLayoutParams`\n  - `StageGlobalPropsBuilder`\n  - `StudioGridSpec`\n  - `StudioLayout`\n  - `StudioResolvedLayout`\n  - `StudioModeGrid`\n  - `StudioViewMode`\n  - `LunaThemeKey`\n  - `LunaThemeMode`\n  - `LunaThemeVariant`\n- Choreography types\n  - `ChoreographyBaseProps`\n  - `ChoreographyProps`\n  - `ChoreographyInteractionProps`\n  - `ChoreographyViewProps`\n  - `InteractionParams`\n  - `InteractionTarget`\n  - `FocusKeyResolver`\n- Lynx bridge types\n  - `LynxRuntimeCall`\n- Helpers\n  - `resolveStudioLayout`\n  - `indexResolvedLayout`\n  - `getPayloadString`\n\n`ChoreographyView` and `StudioLynxStage` exist inside the package as internal implementation layers, but they are not part of the public component API.\n\n## Quick Start\n\n```tsx\nimport { Choreography } from '@dugyu/luna-studio';\nimport type { StudioModeGrid, StudioResolvedLayout } from '@dugyu/luna-studio';\n\nconst modeGrid: StudioModeGrid = {\n  compare: { cols: 2, rows: 1 },\n  focus: { cols: 3, rows: 1 },\n  lineup: { cols: 3, rows: 1 },\n};\n\nconst layout: StudioResolvedLayout = {\n  compare: [\n    {\n      id: 'button-light',\n      entry: 'ActButton',\n      theme: 'luna-light',\n      focusKey: 'button',\n      style: { gridColumn: '1 / span 1', gridRow: '1 / span 1' },\n    },\n    {\n      id: 'button-dark',\n      entry: 'ActButton',\n      theme: 'luna-dark',\n      focusKey: 'button',\n      style: { gridColumn: '2 / span 1', gridRow: '1 / span 1' },\n    },\n  ],\n  focus: [\n    {\n      id: 'button-focus',\n      entry: 'ActButton',\n      theme: 'luna-light',\n      focusKey: 'button',\n      style: { gridColumn: '2 / span 1', gridRow: '1 / span 1' },\n    },\n  ],\n  lineup: [\n    {\n      id: 'button-lineup',\n      entry: 'ActButton',\n      theme: 'lunaris-dark',\n      focusKey: 'button',\n      style: { gridColumn: '2 / span 1', gridRow: '1 / span 1' },\n    },\n  ],\n};\n\nexport function Demo() {\n  return (\n    <div className='size-full'>\n      <Choreography\n        bundleRoot='/bundles/'\n        layout={layout}\n        modeGrid={modeGrid}\n        viewMode='compare'\n        themeKey='lunaris-dark'\n        interactionTarget='content'\n      />\n    </div>\n  );\n}\n```\n\nIf your host prefers an authoring model that separates a resource pool (entry/bundleRoot/theme defaults) from per-mode layout overrides, you can build the resolved layout with `resolveStudioLayout`:\n\n```ts\nimport { resolveStudioLayout } from '@dugyu/luna-studio';\nimport type {\n  ResolveStudioLayoutParams,\n  StudioLayout,\n  StudioStage,\n} from '@dugyu/luna-studio';\n\nconst stagePool: Record<string, StudioStage> = {\n  Button: { id: 'Button', entry: 'ActButton', theme: 'luna-light' },\n};\n\nconst layoutSpec: StudioLayout = {\n  compare: [{ id: 'Button', style: { gridColumn: '1 / 2', gridRow: '1 / 2' } }],\n  focus: [{ id: 'Button', style: { gridColumn: '1 / 2', gridRow: '1 / 2' } }],\n  lineup: [{ id: 'Button', style: { gridColumn: '1 / 2', gridRow: '1 / 2' } }],\n};\n\nconst resolvedLayout = resolveStudioLayout(\n  {\n    stagePool,\n    layoutSpec,\n  } satisfies ResolveStudioLayoutParams,\n);\n```\n\n## Core Concepts\n\n### `StudioStage`\n\n`StudioStage` is the resource/pool definition for a stage (resource locator + defaults).\n\n```ts\ntype StudioStage = {\n  id: string;\n  entry: string;\n  theme: LunaThemeKey;\n  bundleRoot?: string;\n  focusKey?: string;\n};\n```\n\nNotes:\n\n- `entry` and `bundleRoot` are the resource locator used to locate the Lynx bundle.\n- `theme` and `focusKey` are stage-level defaults used during layout resolution.\n\n### `StudioResolvedStage`\n\n`StudioResolvedStage` is the per-stage data boundary consumed by the choreography layer.\n\n```ts\ntype StudioResolvedStage = {\n  id: string;\n  className?: string;\n  style?: CSSProperties;\n  bundleRoot?: string;\n  focusKey?: string;\n  entry: string;\n  theme: LunaThemeKey;\n};\n```\n\nNotes:\n\n- `id` is the stable stage identity used for layout, motion, and interaction correlation.\n- `className` and `style` apply to the stage container.\n- `bundleRoot` can be provided per-stage; otherwise `Choreography.bundleRoot` may act as a host-level fallback.\n- app-specific metadata should live in app-local extension types rather than reopening the public `StudioResolvedStage` contract.\n\n### `StudioStageSlice`\n\n`StudioStageSlice` is the per-mode layout projection for a stage. It references a pool stage by `id` and carries host-side overrides for that mode.\n\n```ts\ntype StudioStageSlice = {\n  id: string;\n  className?: string;\n  style?: CSSProperties;\n  theme?: LunaThemeKey;\n  focusKey?: string;\n};\n```\n\nNotes:\n\n- Slice ids must be unique within the same `StudioViewMode`. `Choreography` uses the resolved `stage.id` as the stable render identity (React key, Motion layout id, and emitted `interaction.stageId`).\n- To render the same Lynx `entry` multiple times, define multiple `StudioStage` pool items with different `id` values but the same `entry`.\n\n### `StudioLayout`\n\n`StudioLayout` is the authoring layout specification for the three built-in presentation modes. Each item references a stage by `id` and carries host-side overrides (e.g., `className`, `style`, `theme`, `focusKey`).\n\n```ts\ntype StudioLayout = Record<\n  'compare' | 'focus' | 'lineup',\n  StudioStageSlice[]\n>;\n```\n\n### `StudioResolvedLayout`\n\n`StudioResolvedLayout` contains one stage list for each built-in presentation mode.\n\n```ts\ntype StudioResolvedLayout = Record<\n  'compare' | 'focus' | 'lineup',\n  StudioResolvedStage[]\n>;\n```\n\n`modeGrid` can optionally provide the grid dimensions for each mode when the host wants the container layout to be grid-driven.\n\n### `StudioViewMode`\n\n`StudioViewMode` selects which layout slice is active.\n\n| Mode      | Behavior                                                        |\n| --------- | --------------------------------------------------------------- |\n| `compare` | Renders multiple stages side-by-side and allows per-stage theme |\n| `focus`   | Renders a shared theme with 3D focal emphasis                   |\n| `lineup`  | Renders a shared theme in the lineup arrangement                |\n\n## Interaction Model\n\n### `InteractionParams`\n\n`InteractionParams` is the normalized interaction envelope used by choreography callbacks.\n\n```ts\ntype InteractionParams =\n  | {\n    target: 'stage';\n    stageId: string;\n    entry: string;\n    payload?: unknown;\n    containerEvent: MouseEvent | PointerEvent;\n  }\n  | {\n    target: 'content';\n    stageId: string;\n    entry: string;\n    payload?: unknown;\n    runtimeCall: LynxRuntimeCall;\n  };\n```\n\nNotes:\n\n- `target: 'stage'` means the interaction is attributed to the host-side stage layer; the raw browser payload is exposed as `containerEvent`.\n- `target: 'content'` means the interaction is attributed to embedded Lynx content; the raw Lynx runtime payload is exposed as `runtimeCall`.\n- `containerEvent.type` distinguishes host-side click / pointer variants.\n- `runtimeCall.name` distinguishes content-side interaction kinds emitted through `onNativeModulesCall`.\n\n### `interactionTarget`\n\n`interactionTarget` controls which layer receives pointer interaction first:\n\n- `'content'`: embedded Lynx content is interactive\n- `'stage'`: the outer stage container is interactive\n\n### `onInteraction`\n\nReceives the normalized choreography interaction envelope.\n\n```ts\ntype OnInteraction = (interaction: InteractionParams) => unknown;\n```\n\nUse this as the primary high-level callback when you want one place to observe both host-side stage-container interactions and embedded-content runtime callbacks. The concrete raw payload is available as `interaction.containerEvent` or `interaction.runtimeCall`, depending on `interaction.target`.\n\n### `onLynxRuntimeCall`\n\nReceives raw runtime callbacks emitted from embedded Lynx content.\n\n```ts\ntype OnLynxRuntimeCall = (call: LynxRuntimeCall) => unknown;\n\ntype LynxRuntimeCall = {\n  entry: string;\n  channel: string;\n  name: string;\n  data: unknown;\n};\n```\n\nUse this only when you need the original Lynx bridge / RPC callback directly, rather than the normalized `onInteraction` abstraction.\n\nThis keeps the package surface runtime-neutral:\n\n- no low-level `moduleName` leak\n- `channel` remains the host-facing source identifier\n- app-specific interpretation stays outside the package\n\n### `resolveFocusKey`\n\n`resolveFocusKey` maps a normalized interaction back to a stage-level `focusKey`.\n\n```ts\ntype FocusKeyResolver = (\n  interaction: InteractionParams,\n) => string | undefined;\n```\n\nThis is the app-owned bridge between generic choreography interactions and business-specific focus semantics.\n\n### `buildStageGlobalProps`\n\n`buildStageGlobalProps` lets the host derive app-specific Lynx global props for each rendered stage.\n\n```ts\ntype StageGlobalPropsBuilder = (params: {\n  stage: StudioStage;\n  viewMode: StudioViewMode;\n  activeFocusKey: string;\n  focusKey: string | undefined;\n}) => Record<string, unknown> | undefined;\n```\n\nThe two focus-related fields serve different roles:\n\n- `activeFocusKey` is the current choreography-wide active focus identity.\n- `focusKey` is the focus identity of the specific `stage` currently being rendered.\n\nIn other words, `activeFocusKey` answers \"which focus target is currently active?\", while `focusKey` answers \"which focus target does this particular stage belong to?\".\n\nThis makes it easy to derive stage-local props such as:\n\n- whether the current stage is the active/focused stage\n- which shared component or semantic key is currently active elsewhere in the choreography\n- app-specific flags that depend on both the rendered stage and the global focus state\n\n## `Choreography` API\n\n`Choreography` is the main public component in this package.\n\nInternally, it wraps `ChoreographyView`, which is the lower-level renderer responsible for layout, focus state, interaction normalization, and Lynx-stage wiring.\n\n```ts\ntype ChoreographyProps = Omit<ChoreographyViewProps, 'mode'> & {\n  viewMode?: StudioViewMode;\n};\n```\n\nBehavior notes:\n\n- `viewMode` defaults to `'compare'`\n- `themeKey` defaults to `'lunaris-dark'`\n- `interactionTarget` defaults to `'content'`\n- `focusKey` is read directly from each resolved stage in `layout` (i.e., from the `StudioResolvedLayout` stage items)\n- `bundleRoot` can provide a choreography-level fallback; per-stage resource roots are taken from each resolved stage in `layout`\n- `layout` is required and should already be fully resolved by the host app\n\n## Scope\n\nThis package covers the reusable choreography layer:\n\n- public choreography types and data model\n- grid-driven multi-stage layout and focus presentation\n- normalized stage/content interaction handling\n- the internal `ChoreographyView` renderer\n- the internal Lynx runtime adapter used by `Choreography`\n\nThis package does **not** provide:\n\n- a top-level `Studio` shell component\n- built-in demo layout data\n- app chrome such as `MenuBar`\n- app-level keyboard bindings\n- demo-specific runtime event parsing\n- a public `lynx-stage` subpath export\n\n## Intended Consumers\n\nUse `@dugyu/luna-studio` when you need a reusable multi-stage choreography layer but want to keep your app shell, layout data, business semantics, and event handling local:\n\n- documentation sites embedding multiple Lynx entries\n- design system comparison canvases\n- host apps that need both stage-container interactions and Lynx runtime callbacks\n- future `lynx-website` integration\n","readmeFilename":"README.md"}