@scaffold-ui-kit/preview 0.0.0-stage → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1868 -2
- package/application/host/bootstrap.ts +43 -0
- package/application/host/main.ts +31 -0
- package/application/host/shell-host.ts +47 -0
- package/application/index.html +12 -0
- package/application/lib/base-url.ts +15 -0
- package/application/lib/no-code-block.ts +7 -0
- package/application/lib/no-setup.ts +3 -0
- package/application/lib/no-views.ts +6 -0
- package/application/preview/code/code.html +12 -0
- package/application/preview/code/main.ts +89 -0
- package/application/preview/globals.ts +254 -0
- package/application/preview/html/main.ts +93 -0
- package/application/preview/html/preview.html +12 -0
- package/application/preview/react/main.tsx +146 -0
- package/application/preview/react/preview.html +12 -0
- package/application/preview/svelte/Preview.svelte +104 -0
- package/application/preview/svelte/main.ts +13 -0
- package/application/preview/svelte/preview.html +12 -0
- package/application/preview/svelte/story-props.ts +172 -0
- package/application/preview/view/main.ts +124 -0
- package/application/preview/view/view.html +12 -0
- package/application/preview/vue/main.ts +145 -0
- package/application/preview/vue/preview.html +12 -0
- package/application/shells/charcell/components/Application/index.css +37 -0
- package/application/shells/charcell/components/Application/index.tsx +65 -0
- package/application/shells/charcell/components/Bar/index.css +68 -0
- package/application/shells/charcell/components/Bar/index.tsx +60 -0
- package/application/shells/charcell/components/CodePane/index.css +60 -0
- package/application/shells/charcell/components/CodePane/index.tsx +113 -0
- package/application/shells/charcell/components/ComponentStage/index.css +26 -0
- package/application/shells/charcell/components/ComponentStage/index.tsx +110 -0
- package/application/shells/charcell/components/Control/index.css +20 -0
- package/application/shells/charcell/components/Control/index.tsx +118 -0
- package/application/shells/charcell/components/CopyLink/index.tsx +35 -0
- package/application/shells/charcell/components/Description/index.css +6 -0
- package/application/shells/charcell/components/Description/index.tsx +24 -0
- package/application/shells/charcell/components/EditorAddDialog/index.css +9 -0
- package/application/shells/charcell/components/EditorAddDialog/index.tsx +158 -0
- package/application/shells/charcell/components/EditorPanel/index.css +48 -0
- package/application/shells/charcell/components/EditorPanel/index.tsx +496 -0
- package/application/shells/charcell/components/EventsRow/index.css +42 -0
- package/application/shells/charcell/components/EventsRow/index.tsx +68 -0
- package/application/shells/charcell/components/HeadingRow/index.css +11 -0
- package/application/shells/charcell/components/HeadingRow/index.tsx +16 -0
- package/application/shells/charcell/components/Hint/index.css +16 -0
- package/application/shells/charcell/components/Hint/index.tsx +81 -0
- package/application/shells/charcell/components/HtmlWarnings/index.tsx +45 -0
- package/application/shells/charcell/components/KeyHints/index.css +27 -0
- package/application/shells/charcell/components/KeyHints/index.tsx +106 -0
- package/application/shells/charcell/components/KitCodeBlock/index.css +55 -0
- package/application/shells/charcell/components/KitCodeBlock/index.tsx +18 -0
- package/application/shells/charcell/components/Markdown/index.tsx +16 -0
- package/application/shells/charcell/components/Navigation/index.css +13 -0
- package/application/shells/charcell/components/Navigation/index.tsx +98 -0
- package/application/shells/charcell/components/NavigationSplit/index.css +16 -0
- package/application/shells/charcell/components/NavigationSplit/index.tsx +82 -0
- package/application/shells/charcell/components/OptionControls/index.tsx +93 -0
- package/application/shells/charcell/components/PaneStatus/index.css +7 -0
- package/application/shells/charcell/components/PaneStatus/index.tsx +47 -0
- package/application/shells/charcell/components/PaneWidth/index.tsx +45 -0
- package/application/shells/charcell/components/PreviewGrid/index.css +39 -0
- package/application/shells/charcell/components/PreviewGrid/index.tsx +344 -0
- package/application/shells/charcell/components/PreviewPane/index.css +127 -0
- package/application/shells/charcell/components/PreviewPane/index.tsx +182 -0
- package/application/shells/charcell/components/PromptBar/index.tsx +133 -0
- package/application/shells/charcell/components/ScenarioRow/index.css +36 -0
- package/application/shells/charcell/components/ScenarioRow/index.tsx +71 -0
- package/application/shells/charcell/components/Select/index.tsx +48 -0
- package/application/shells/charcell/components/SlotOutlet/index.css +7 -0
- package/application/shells/charcell/components/SlotOutlet/index.tsx +25 -0
- package/application/shells/charcell/components/StageBar/index.css +7 -0
- package/application/shells/charcell/components/StageBar/index.tsx +109 -0
- package/application/shells/charcell/components/StageSplit/index.css +11 -0
- package/application/shells/charcell/components/StageSplit/index.tsx +99 -0
- package/application/shells/charcell/components/StatusBar/index.tsx +30 -0
- package/application/shells/charcell/components/ViewControls/index.css +15 -0
- package/application/shells/charcell/components/ViewControls/index.tsx +58 -0
- package/application/shells/charcell/components/ViewHeading/index.css +13 -0
- package/application/shells/charcell/components/ViewHeading/index.tsx +8 -0
- package/application/shells/charcell/components/ViewStage/index.css +9 -0
- package/application/shells/charcell/components/ViewStage/index.tsx +25 -0
- package/application/shells/charcell/components/WrapToggle/index.tsx +27 -0
- package/application/shells/charcell/configuration.ts +45 -0
- package/application/shells/charcell/controller-context.ts +27 -0
- package/application/shells/charcell/fit-labels.ts +42 -0
- package/application/shells/charcell/html-warnings.ts +18 -0
- package/application/shells/charcell/index.tsx +82 -0
- package/application/shells/charcell/page-loading.html +57 -0
- package/application/shells/charcell/placement.ts +24 -0
- package/application/shells/charcell/shell-options.ts +23 -0
- package/application/shells/charcell/shell.css +37 -0
- package/application/shells/charcell/shell.tsx +25 -0
- package/application/shells/charcell/split-reset.ts +47 -0
- package/application/shells/charcell/stacked-rows.ts +117 -0
- package/application/shells/charcell/stage-toggles.ts +93 -0
- package/application/types/manifest.d.ts +6 -0
- package/application/types/preview-configuration.d.ts +10 -0
- package/application/types/preview-shell.d.ts +7 -0
- package/application/types/react-registry.d.ts +15 -0
- package/application/types/vue-registry.d.ts +6 -0
- package/dist/build.d.ts +24 -0
- package/dist/build.js +34 -0
- package/dist/check.d.ts +48 -0
- package/dist/check.js +781 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +88 -0
- package/dist/context.d.ts +179 -0
- package/dist/context.js +783 -0
- package/dist/controller/catalog.d.ts +339 -0
- package/dist/controller/catalog.js +367 -0
- package/dist/controller/create-preview-controller.d.ts +532 -0
- package/dist/controller/create-preview-controller.js +1707 -0
- package/dist/controller/editor-tree.d.ts +161 -0
- package/dist/controller/editor-tree.js +373 -0
- package/dist/controller/html-source.d.ts +62 -0
- package/dist/controller/html-source.js +61 -0
- package/dist/controller/index.d.ts +9 -0
- package/dist/controller/index.js +12 -0
- package/dist/controller/persistence.d.ts +191 -0
- package/dist/controller/persistence.js +315 -0
- package/dist/controller/regeneration.d.ts +30 -0
- package/dist/controller/regeneration.js +27 -0
- package/dist/controller/source.d.ts +80 -0
- package/dist/controller/source.js +236 -0
- package/dist/controller/store.d.ts +27 -0
- package/dist/controller/store.js +40 -0
- package/dist/declarations/application/shells/charcell/components/Application/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Bar/index.d.ts +11 -0
- package/dist/declarations/application/shells/charcell/components/CodePane/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/ComponentStage/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Control/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/CopyLink/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/Description/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/EditorAddDialog/index.d.ts +6 -0
- package/dist/declarations/application/shells/charcell/components/EditorPanel/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/EventsRow/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/HeadingRow/index.d.ts +6 -0
- package/dist/declarations/application/shells/charcell/components/Hint/index.d.ts +11 -0
- package/dist/declarations/application/shells/charcell/components/HtmlWarnings/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/KeyHints/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/KitCodeBlock/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/Markdown/index.d.ts +3 -0
- package/dist/declarations/application/shells/charcell/components/Navigation/index.d.ts +8 -0
- package/dist/declarations/application/shells/charcell/components/NavigationSplit/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/OptionControls/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/PaneStatus/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/PaneWidth/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/PreviewGrid/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/PreviewPane/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/PromptBar/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/ScenarioRow/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Select/index.d.ts +16 -0
- package/dist/declarations/application/shells/charcell/components/SlotOutlet/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/StageBar/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/StageSplit/index.d.ts +17 -0
- package/dist/declarations/application/shells/charcell/components/StatusBar/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/ViewControls/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/ViewHeading/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/ViewStage/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/WrapToggle/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/configuration.d.ts +31 -0
- package/dist/declarations/application/shells/charcell/controller-context.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/fit-labels.d.ts +18 -0
- package/dist/declarations/application/shells/charcell/html-warnings.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/index.d.ts +39 -0
- package/dist/declarations/application/shells/charcell/placement.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/shell-options.d.ts +15 -0
- package/dist/declarations/application/shells/charcell/shell.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/split-reset.d.ts +30 -0
- package/dist/declarations/application/shells/charcell/stacked-rows.d.ts +58 -0
- package/dist/declarations/application/shells/charcell/stage-toggles.d.ts +28 -0
- package/dist/declarations/src/controller/catalog.d.ts +339 -0
- package/dist/declarations/src/controller/create-preview-controller.d.ts +532 -0
- package/dist/declarations/src/controller/editor-tree.d.ts +161 -0
- package/dist/declarations/src/controller/html-source.d.ts +62 -0
- package/dist/declarations/src/controller/index.d.ts +9 -0
- package/dist/declarations/src/controller/persistence.d.ts +191 -0
- package/dist/declarations/src/controller/regeneration.d.ts +30 -0
- package/dist/declarations/src/controller/source.d.ts +80 -0
- package/dist/declarations/src/controller/store.d.ts +27 -0
- package/dist/declarations/src/emit/common.d.ts +103 -0
- package/dist/declarations/src/emit/manifest.d.ts +138 -0
- package/dist/declarations/src/emit/source.d.ts +87 -0
- package/dist/declarations/src/lib/jsx-roots.d.ts +22 -0
- package/dist/declarations/src/lib/module-of-source.d.ts +18 -0
- package/dist/declarations/src/lib/naming.d.ts +29 -0
- package/dist/declarations/src/lib/react-spelling.d.ts +68 -0
- package/dist/declarations/src/lib/scenario-story.d.ts +41 -0
- package/dist/declarations/src/lib/slot-fill.d.ts +15 -0
- package/dist/declarations/src/lib/types.d.ts +741 -0
- package/dist/declarations/src/options.d.ts +224 -0
- package/dist/declarations/src/protocol.d.ts +541 -0
- package/dist/declarations/src/react-view.d.ts +22 -0
- package/dist/declarations/src/react.d.ts +17 -0
- package/dist/declarations/src/shell.d.ts +99 -0
- package/dist/declarations/src/views.d.ts +152 -0
- package/dist/development-server.d.ts +121 -0
- package/dist/development-server.js +331 -0
- package/dist/emit/common.d.ts +103 -0
- package/dist/emit/common.js +170 -0
- package/dist/emit/html-source.d.ts +46 -0
- package/dist/emit/html-source.js +322 -0
- package/dist/emit/manifest.d.ts +138 -0
- package/dist/emit/manifest.js +74 -0
- package/dist/emit/react.d.ts +16 -0
- package/dist/emit/react.js +210 -0
- package/dist/emit/scratch.d.ts +40 -0
- package/dist/emit/scratch.js +116 -0
- package/dist/emit/source.d.ts +87 -0
- package/dist/emit/source.js +789 -0
- package/dist/emit/svelte.d.ts +9 -0
- package/dist/emit/svelte.js +177 -0
- package/dist/emit/vue.d.ts +2 -0
- package/dist/emit/vue.js +152 -0
- package/dist/enumerate.d.ts +2 -0
- package/dist/enumerate.js +162 -0
- package/dist/generate.d.ts +43 -0
- package/dist/generate.js +302 -0
- package/dist/html-stylesheet.d.ts +43 -0
- package/dist/html-stylesheet.js +145 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +21 -0
- package/dist/landing.d.ts +16 -0
- package/dist/landing.js +45 -0
- package/dist/lib/exported-types.d.ts +42 -0
- package/dist/lib/exported-types.js +86 -0
- package/dist/lib/html-attributes.d.ts +2 -0
- package/dist/lib/html-attributes.js +111 -0
- package/dist/lib/html-warnings.d.ts +19 -0
- package/dist/lib/html-warnings.js +79 -0
- package/dist/lib/jsx-roots.d.ts +22 -0
- package/dist/lib/jsx-roots.js +243 -0
- package/dist/lib/module-of-source.d.ts +18 -0
- package/dist/lib/module-of-source.js +24 -0
- package/dist/lib/naming.d.ts +29 -0
- package/dist/lib/naming.js +56 -0
- package/dist/lib/parse-type.d.ts +32 -0
- package/dist/lib/parse-type.js +103 -0
- package/dist/lib/react-spelling.d.ts +68 -0
- package/dist/lib/react-spelling.js +197 -0
- package/dist/lib/scenario-story.d.ts +41 -0
- package/dist/lib/scenario-story.js +84 -0
- package/dist/lib/slot-fill.d.ts +15 -0
- package/dist/lib/slot-fill.js +92 -0
- package/dist/lib/template-files.d.ts +14 -0
- package/dist/lib/template-files.js +25 -0
- package/dist/lib/types.d.ts +741 -0
- package/dist/lib/types.js +13 -0
- package/dist/lib/void-elements.d.ts +7 -0
- package/dist/lib/void-elements.js +13 -0
- package/dist/load.d.ts +37 -0
- package/dist/load.js +82 -0
- package/dist/model.d.ts +44 -0
- package/dist/model.js +464 -0
- package/dist/options.d.ts +224 -0
- package/dist/options.js +223 -0
- package/dist/prop-types.d.ts +26 -0
- package/dist/prop-types.js +102 -0
- package/dist/props-from.d.ts +84 -0
- package/dist/props-from.js +510 -0
- package/dist/protocol.d.ts +541 -0
- package/dist/protocol.js +819 -0
- package/dist/react-view.d.ts +22 -0
- package/dist/react-view.js +36 -0
- package/dist/react.d.ts +17 -0
- package/dist/react.js +23 -0
- package/dist/sass-importers.d.ts +26 -0
- package/dist/sass-importers.js +32 -0
- package/dist/shell-acceptance.d.ts +21 -0
- package/dist/shell-acceptance.js +37 -0
- package/dist/shell-page.d.ts +36 -0
- package/dist/shell-page.js +90 -0
- package/dist/shell-peers.d.ts +38 -0
- package/dist/shell-peers.js +61 -0
- package/dist/shell.d.ts +99 -0
- package/dist/shell.js +52 -0
- package/dist/shells/charcell/page.d.ts +8 -0
- package/dist/shells/charcell/page.js +69 -0
- package/dist/story-source.d.ts +113 -0
- package/dist/story-source.js +271 -0
- package/dist/story.d.ts +5 -0
- package/dist/story.js +28 -0
- package/dist/testing.d.ts +218 -0
- package/dist/testing.js +284 -0
- package/dist/views.d.ts +152 -0
- package/dist/views.js +7 -0
- package/dist/vite-configuration.d.ts +114 -0
- package/dist/vite-configuration.js +578 -0
- package/package.json +123 -3
- package/src/build.ts +51 -0
- package/src/check.ts +1062 -0
- package/src/cli.ts +123 -0
- package/src/context.ts +1168 -0
- package/src/controller/catalog.ts +719 -0
- package/src/controller/create-preview-controller.ts +2683 -0
- package/src/controller/editor-tree.ts +530 -0
- package/src/controller/html-source.ts +114 -0
- package/src/controller/index.ts +105 -0
- package/src/controller/persistence.ts +403 -0
- package/src/controller/regeneration.ts +55 -0
- package/src/controller/source.ts +333 -0
- package/src/controller/store.ts +55 -0
- package/src/development-server.ts +442 -0
- package/src/emit/common.ts +201 -0
- package/src/emit/html-source.ts +402 -0
- package/src/emit/manifest.ts +241 -0
- package/src/emit/react.ts +263 -0
- package/src/emit/scratch.ts +175 -0
- package/src/emit/source.ts +1094 -0
- package/src/emit/svelte.ts +230 -0
- package/src/emit/vue.ts +194 -0
- package/src/enumerate.ts +220 -0
- package/src/generate.ts +443 -0
- package/src/html-stylesheet.ts +173 -0
- package/src/index.ts +60 -0
- package/src/landing.ts +58 -0
- package/src/lib/exported-types.ts +114 -0
- package/src/lib/html-attributes.ts +113 -0
- package/src/lib/html-warnings.ts +81 -0
- package/src/lib/jsx-roots.ts +238 -0
- package/src/lib/module-of-source.ts +26 -0
- package/src/lib/naming.ts +65 -0
- package/src/lib/parse-type.ts +143 -0
- package/src/lib/react-spelling.ts +235 -0
- package/src/lib/scenario-story.ts +97 -0
- package/src/lib/slot-fill.ts +102 -0
- package/src/lib/template-files.ts +31 -0
- package/src/lib/types.ts +778 -0
- package/src/lib/void-elements.ts +15 -0
- package/src/load.ts +144 -0
- package/src/model.ts +568 -0
- package/src/options.ts +435 -0
- package/src/prop-types.ts +133 -0
- package/src/props-from.ts +678 -0
- package/src/protocol.ts +1135 -0
- package/src/react-view.ts +45 -0
- package/src/react.ts +34 -0
- package/src/sass-importers.ts +45 -0
- package/src/shell-acceptance.ts +45 -0
- package/src/shell-page.ts +133 -0
- package/src/shell-peers.ts +70 -0
- package/src/shell.ts +194 -0
- package/src/shells/charcell/page.ts +82 -0
- package/src/story-source.ts +391 -0
- package/src/story.ts +42 -0
- package/src/testing.ts +497 -0
- package/src/views.ts +183 -0
- package/src/vite-configuration.ts +675 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// React preview entry. Mounts the generated render-registry by id, holds props in
|
|
2
|
+
// state, and updates reactively from the host over postMessage - the same App
|
|
3
|
+
// instance re-renders, so the Zag driver machine stays alive across prop changes.
|
|
4
|
+
import { Component, useEffect, useState, type ReactNode } from 'react';
|
|
5
|
+
import { createRoot } from 'react-dom/client';
|
|
6
|
+
import { declaredAttributes, reactAttributeNames, registry } from '@preview-generated/react/registry';
|
|
7
|
+
// The scenario editor's regenerated scratch story, merged under its reserved id.
|
|
8
|
+
// Its module is a placeholder registry until the editor's first edit.
|
|
9
|
+
import * as scratch from '@preview-generated/scratch/react';
|
|
10
|
+
// The kit's per-target setup module (side effects only): ambient providers a
|
|
11
|
+
// story needs mounted once (a toast container), another package's stylesheets, and any
|
|
12
|
+
// other integration the kit's react stories assume. Aliased to the kit's
|
|
13
|
+
// `preview.setupModules.react` (or an empty module).
|
|
14
|
+
import '@preview-setup/react';
|
|
15
|
+
import { applyGlobals } from '../globals';
|
|
16
|
+
import {
|
|
17
|
+
announcePreviewReady,
|
|
18
|
+
announcePreviewShown,
|
|
19
|
+
announcePreviewStories,
|
|
20
|
+
announceStoryMounted,
|
|
21
|
+
PREVIEW_MESSAGE,
|
|
22
|
+
PREVIEW_MOUNT_ELEMENT_ID,
|
|
23
|
+
readPreviewMessage,
|
|
24
|
+
reportContentSize,
|
|
25
|
+
reportRenderError,
|
|
26
|
+
withEventRecorders,
|
|
27
|
+
withWriteBacks,
|
|
28
|
+
PendingWriteBacks,
|
|
29
|
+
type PreviewWriteBack,
|
|
30
|
+
} from '../../../src/protocol';
|
|
31
|
+
import { reactProps } from '../../../src/lib/react-spelling';
|
|
32
|
+
|
|
33
|
+
// What a story threw as a message; it need not have thrown an Error.
|
|
34
|
+
function errorMessage(error: unknown): string {
|
|
35
|
+
return error instanceof Error ? error.message : String(error);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Isolate a throwing component so it can't poison the persistent root (which
|
|
39
|
+
// would blank every later story). Reset per story via a `key` on the boundary;
|
|
40
|
+
// the host hears of the error, and of each story mounted since.
|
|
41
|
+
class ErrorBoundary extends Component<{ children: ReactNode }, { message: string | null }> {
|
|
42
|
+
state = { message: null as string | null };
|
|
43
|
+
static getDerivedStateFromError(error: unknown) {
|
|
44
|
+
return { message: errorMessage(error) };
|
|
45
|
+
}
|
|
46
|
+
componentDidCatch(error: unknown) {
|
|
47
|
+
reportRenderError(errorMessage(error));
|
|
48
|
+
}
|
|
49
|
+
render() {
|
|
50
|
+
if (this.state.message !== null) return <pre>render error: {this.state.message}</pre>;
|
|
51
|
+
return this.props.children;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The values this pane has written back itself and the host has not yet
|
|
56
|
+
// answered: props the host sent before it took the newest keep that value.
|
|
57
|
+
const pending = new PendingWriteBacks();
|
|
58
|
+
|
|
59
|
+
function Application() {
|
|
60
|
+
const [id, setId] = useState('');
|
|
61
|
+
const [props, setProps] = useState<Record<string, unknown>>({});
|
|
62
|
+
// The story's events, recorded with each set of props it gets.
|
|
63
|
+
const [events, setEvents] = useState<string[]>([]);
|
|
64
|
+
// The story's two-way props, each written back as its event reports a value.
|
|
65
|
+
const [bindings, setBindings] = useState<PreviewWriteBack[]>([]);
|
|
66
|
+
// Each story or scenario the host sets mounts fresh, so uncontrolled state
|
|
67
|
+
// never carries over from the last one; live prop edits update in place.
|
|
68
|
+
const [mount, setMount] = useState(0);
|
|
69
|
+
// The host's mount of the story, which each recorded event carries.
|
|
70
|
+
const [hostMount, setHostMount] = useState<number | undefined>(undefined);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
function onMessage(event: MessageEvent) {
|
|
74
|
+
const message = readPreviewMessage(event.data);
|
|
75
|
+
if (!message) return;
|
|
76
|
+
if (message.type === PREVIEW_MESSAGE.setStory) {
|
|
77
|
+
reportRenderError();
|
|
78
|
+
pending.clear();
|
|
79
|
+
setId(message.id);
|
|
80
|
+
setProps(message.props ?? {});
|
|
81
|
+
setEvents(message.events ?? []);
|
|
82
|
+
setBindings(message.bindings ?? []);
|
|
83
|
+
setHostMount(message.mount);
|
|
84
|
+
setMount((count) => count + 1);
|
|
85
|
+
} else if (message.type === PREVIEW_MESSAGE.props) {
|
|
86
|
+
setProps(pending.reconcile(message.props, message.answers));
|
|
87
|
+
} else if (message.type === PREVIEW_MESSAGE.globals) {
|
|
88
|
+
applyGlobals(message.options);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
window.addEventListener('message', onMessage);
|
|
92
|
+
announcePreviewStories(Object.keys({ ...registry, ...scratch.registry }));
|
|
93
|
+
announcePreviewReady();
|
|
94
|
+
return () => window.removeEventListener('message', onMessage);
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
// The first story the host sets, once painted, lets the host show the pane.
|
|
98
|
+
// The story's own effects have run by now, so a test can read the mount.
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (mount === 0) return;
|
|
101
|
+
announcePreviewShown();
|
|
102
|
+
announceStoryMounted(id);
|
|
103
|
+
}, [mount]);
|
|
104
|
+
|
|
105
|
+
const render = { ...registry, ...scratch.registry }[id];
|
|
106
|
+
if (!render) return null;
|
|
107
|
+
// A story spells the attributes it gives the component as HTML does:
|
|
108
|
+
// React takes `class` as `className`, a `style` string as an object and
|
|
109
|
+
// the attributes it renames by their React names, unless the component
|
|
110
|
+
// declares a prop by that name.
|
|
111
|
+
const declared = { ...declaredAttributes, ...scratch.declaredAttributes }[id] ?? [];
|
|
112
|
+
// A controlled story takes a two-way prop's new value here, in the event
|
|
113
|
+
// that reports it, before React restores a controlled input to its prop's
|
|
114
|
+
// value: typing mid-text keeps the caret where it is. The host's
|
|
115
|
+
// write-back follows with the same value. Each write is numbered, the
|
|
116
|
+
// event reporting it carries the number, and props the host sent before
|
|
117
|
+
// it took the newest keep the pane's own value (`pending`).
|
|
118
|
+
const story = withWriteBacks(withEventRecorders(props, events, window, hostMount, pending), bindings, (prop, value) => {
|
|
119
|
+
pending.wrote(prop, value);
|
|
120
|
+
setProps((current) => (current[prop] === undefined ? current : { ...current, [prop]: value }));
|
|
121
|
+
});
|
|
122
|
+
const spelled = reactProps(story, (name) => declared.includes(name), reactAttributeNames ?? {});
|
|
123
|
+
return <ErrorBoundary key={`${id}:${mount}`}>{render(spelled)}</ErrorBoundary>;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Cache the root across HMR: when the editor's scratch registry hot-updates,
|
|
127
|
+
// Fast Refresh re-runs this module - reuse the existing root (and re-render Application so
|
|
128
|
+
// it picks up the new scratch registry) instead of calling createRoot twice.
|
|
129
|
+
const container = document.getElementById(PREVIEW_MOUNT_ELEMENT_ID)!;
|
|
130
|
+
const store = window as unknown as {
|
|
131
|
+
__previewRoot?: ReturnType<typeof createRoot>;
|
|
132
|
+
};
|
|
133
|
+
const root = store.__previewRoot ?? (store.__previewRoot = createRoot(container));
|
|
134
|
+
root.render(<Application />);
|
|
135
|
+
// The story's size reaches the host on each change, so a shell can size the pane to it.
|
|
136
|
+
reportContentSize(container);
|
|
137
|
+
|
|
138
|
+
// The editor regenerates .preview-generated/scratch/react.tsx on each edit. Self-accept
|
|
139
|
+
// that dep (a no-op) so Vite scopes the change to *this* iframe instead of
|
|
140
|
+
// broadcasting a full-reload to every client on the shared dev server - which
|
|
141
|
+
// would wipe the host's editor state. The actual refresh is host-driven: after a
|
|
142
|
+
// regeneration it bumps this iframe's src, reloading it to pick up the new (invalidated)
|
|
143
|
+
// scratch module - deterministic, unlike relying on Vite's HMR event delivery.
|
|
144
|
+
if (import.meta.hot) {
|
|
145
|
+
import.meta.hot.accept('@preview-generated/scratch/react', () => {});
|
|
146
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>preview - react</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Runes-mode host for the Svelte preview. The generated wrappers (registry
|
|
3
|
+
// values) are runes components. The wrapper gets one object for the
|
|
4
|
+
// story's props that never changes, each of whose props is a signal of its
|
|
5
|
+
// own: an update sets the props that changed alone - a prop update on the
|
|
6
|
+
// same instance, not a remount, keeping the Zag driver machine alive - and
|
|
7
|
+
// a prop the story leaves out is never set, so a component keeps its own
|
|
8
|
+
// two-way value while another prop changes.
|
|
9
|
+
// Each story or scenario the host sets mounts fresh (`mount`), so
|
|
10
|
+
// uncontrolled state never carries over from the last one. An error the
|
|
11
|
+
// story throws shows in its place, as the React page's error boundary
|
|
12
|
+
// shows one, rather than an empty pane, and the host hears of it, and of
|
|
13
|
+
// each story mounted since.
|
|
14
|
+
import { flushSync } from 'svelte';
|
|
15
|
+
import { registry } from '@preview-generated/svelte/registry';
|
|
16
|
+
// The scenario editor's regenerated scratch story, merged under its reserved id.
|
|
17
|
+
import { registry as scratchRegistry } from '@preview-generated/scratch/registry';
|
|
18
|
+
import { applyGlobals } from '../globals';
|
|
19
|
+
import { paneProps, storyProps, type GivenProps } from './story-props';
|
|
20
|
+
import {
|
|
21
|
+
announcePreviewReady,
|
|
22
|
+
announcePreviewShown,
|
|
23
|
+
announcePreviewStories,
|
|
24
|
+
announceStoryMounted,
|
|
25
|
+
PREVIEW_MESSAGE,
|
|
26
|
+
readPreviewMessage,
|
|
27
|
+
reportRenderError,
|
|
28
|
+
} from '../../../src/protocol';
|
|
29
|
+
|
|
30
|
+
const all = { ...registry, ...scratchRegistry };
|
|
31
|
+
|
|
32
|
+
let id = $state('');
|
|
33
|
+
let mount = $state(0);
|
|
34
|
+
|
|
35
|
+
// The story's props, each a signal of its own (see story-props.ts).
|
|
36
|
+
const given = $state<GivenProps>({});
|
|
37
|
+
const props = storyProps(given);
|
|
38
|
+
// The story's events recorded and its two-way props written back as they
|
|
39
|
+
// report a value, props the host sent before it took the newest keeping
|
|
40
|
+
// the pane's own value (see story-props.ts).
|
|
41
|
+
const story = paneProps(given);
|
|
42
|
+
|
|
43
|
+
// Apply what is pending now, so what follows sees the story mounted or
|
|
44
|
+
// gone. An error the flush lets through (one a story throws as it unmounts,
|
|
45
|
+
// past the boundary) is the host's to hear of, not the pane's to stop on.
|
|
46
|
+
function flush(): void {
|
|
47
|
+
try {
|
|
48
|
+
flushSync();
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(error);
|
|
51
|
+
reportRenderError(error instanceof Error ? error.message : String(error));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
$effect(() => {
|
|
56
|
+
function onMessage(event: MessageEvent) {
|
|
57
|
+
const message = readPreviewMessage(event.data);
|
|
58
|
+
if (!message) return;
|
|
59
|
+
if (message.type === PREVIEW_MESSAGE.setStory) {
|
|
60
|
+
reportRenderError();
|
|
61
|
+
// The story mounted comes down before its props change: set first,
|
|
62
|
+
// the next story's props would reach the one still mounted as it
|
|
63
|
+
// unmounts, and a story that reads a prop it needs then, as one
|
|
64
|
+
// stopping a headless machine does, would read it gone and throw.
|
|
65
|
+
id = '';
|
|
66
|
+
flush();
|
|
67
|
+
story.setStory(message.props ?? {}, message);
|
|
68
|
+
id = message.id;
|
|
69
|
+
mount += 1;
|
|
70
|
+
// Mounted, its effects run: the host may show the pane, and a test
|
|
71
|
+
// may read the mount. Reported here rather than from an effect of
|
|
72
|
+
// this component, which an error let through would stop for good.
|
|
73
|
+
flush();
|
|
74
|
+
announcePreviewShown();
|
|
75
|
+
announceStoryMounted(message.id);
|
|
76
|
+
} else if (message.type === PREVIEW_MESSAGE.props) {
|
|
77
|
+
story.setProps(message.props, message.answers);
|
|
78
|
+
} else if (message.type === PREVIEW_MESSAGE.globals) {
|
|
79
|
+
applyGlobals(message.options);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
window.addEventListener('message', onMessage);
|
|
83
|
+
announcePreviewStories(Object.keys(all));
|
|
84
|
+
announcePreviewReady();
|
|
85
|
+
return () => window.removeEventListener('message', onMessage);
|
|
86
|
+
});
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
{#if id && all[id]}
|
|
90
|
+
{@const Comp = all[id]}
|
|
91
|
+
{#key mount}
|
|
92
|
+
<svelte:boundary
|
|
93
|
+
onerror={(error) => {
|
|
94
|
+
console.error(error);
|
|
95
|
+
reportRenderError(error instanceof Error ? error.message : String(error));
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<Comp {props} />
|
|
99
|
+
{#snippet failed(error)}
|
|
100
|
+
<pre>render error: {error instanceof Error ? error.message : String(error)}</pre>
|
|
101
|
+
{/snippet}
|
|
102
|
+
</svelte:boundary>
|
|
103
|
+
{/key}
|
|
104
|
+
{/if}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Svelte preview entry. The postMessage wiring + reactive props live in
|
|
2
|
+
// Preview.svelte (runes); this just mounts it. The theme stylesheet is loaded
|
|
3
|
+
// by globals.ts (imported via Preview.svelte).
|
|
4
|
+
// The kit's per-target setup module (side effects only) - see react/main.tsx.
|
|
5
|
+
import '@preview-setup/svelte';
|
|
6
|
+
import { mount } from 'svelte';
|
|
7
|
+
import Preview from './Preview.svelte';
|
|
8
|
+
import { PREVIEW_MOUNT_ELEMENT_ID, reportContentSize } from '../../../src/protocol';
|
|
9
|
+
|
|
10
|
+
const target = document.getElementById(PREVIEW_MOUNT_ELEMENT_ID)!;
|
|
11
|
+
mount(Preview, { target });
|
|
12
|
+
// The story's size reaches the host on each change, so a shell can size the pane to it.
|
|
13
|
+
reportContentSize(target);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>preview - svelte</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// The Svelte pane's story props: one object for the component's props that
|
|
2
|
+
// never changes, each of whose props reads a signal of its own. An update
|
|
3
|
+
// sets the props whose values changed alone, and a prop the story leaves out
|
|
4
|
+
// is never set, as a parent leaves it. Were the whole object replaced, every
|
|
5
|
+
// prop would read as set again, and a component's own two-way value (a
|
|
6
|
+
// bindable the story leaves out) would go back to its default whenever
|
|
7
|
+
// another prop changed: Svelte keeps a prop the component assigns only until
|
|
8
|
+
// the prop is read anew. An array or object the story gives again with the
|
|
9
|
+
// same content, as each message from the host does, is the same value: the
|
|
10
|
+
// host compares values the same way.
|
|
11
|
+
|
|
12
|
+
import { stableStringify } from '../../../src/controller/editor-tree';
|
|
13
|
+
import {
|
|
14
|
+
isPlainData,
|
|
15
|
+
PendingWriteBacks,
|
|
16
|
+
withEventRecorders,
|
|
17
|
+
withWriteBacks,
|
|
18
|
+
type PreviewWriteBack,
|
|
19
|
+
} from '../../../src/protocol';
|
|
20
|
+
|
|
21
|
+
/** A prop's value in a box, so it stays the value it is: a `$state` record
|
|
22
|
+
* wraps an array or plain object in a proxy of its own, a class instance not. */
|
|
23
|
+
export class GivenProp {
|
|
24
|
+
constructor(readonly value: unknown) {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** The props a story gives, by name, each in a box: a `$state` record, so
|
|
28
|
+
* each prop is a signal of its own. */
|
|
29
|
+
export type GivenProps = Record<string, GivenProp>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The component's props, read from `given`: an object that never changes,
|
|
33
|
+
* whose each prop reads its own box, so a read of one prop depends on that
|
|
34
|
+
* prop alone.
|
|
35
|
+
*
|
|
36
|
+
* @param given - The story's props, a `$state` record of boxes.
|
|
37
|
+
* @returns The props to spread on the component.
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const given = $state<GivenProps>({});
|
|
41
|
+
* const props = storyProps(given);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function storyProps(given: GivenProps): Record<string, unknown> {
|
|
45
|
+
return new Proxy(
|
|
46
|
+
{},
|
|
47
|
+
{
|
|
48
|
+
get: (_, key) =>
|
|
49
|
+
typeof key === 'string' ? given[key]?.value : undefined,
|
|
50
|
+
has: (_, key) => typeof key === 'string' && key in given,
|
|
51
|
+
ownKeys: () => Reflect.ownKeys(given),
|
|
52
|
+
getOwnPropertyDescriptor: (_, key) =>
|
|
53
|
+
typeof key === 'string' && key in given
|
|
54
|
+
? {
|
|
55
|
+
value: given[key].value,
|
|
56
|
+
enumerable: true,
|
|
57
|
+
configurable: true,
|
|
58
|
+
writable: true,
|
|
59
|
+
}
|
|
60
|
+
: undefined,
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether a prop's value is the one it had: the same value, or arrays or
|
|
67
|
+
* plain objects of plain data holding the same content.
|
|
68
|
+
*
|
|
69
|
+
* @param previous - The value it had.
|
|
70
|
+
* @param next - The value it is given.
|
|
71
|
+
* @returns `true` when nothing changed.
|
|
72
|
+
*/
|
|
73
|
+
export function sameValue(previous: unknown, next: unknown): boolean {
|
|
74
|
+
if (Object.is(previous, next)) return true;
|
|
75
|
+
return (
|
|
76
|
+
typeof previous === 'object' &&
|
|
77
|
+
previous !== null &&
|
|
78
|
+
typeof next === 'object' &&
|
|
79
|
+
next !== null &&
|
|
80
|
+
isPlainData(previous) &&
|
|
81
|
+
isPlainData(next) &&
|
|
82
|
+
stableStringify(previous) === stableStringify(next)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Set the story's props to `next`: a prop whose value changed gets a new box,
|
|
88
|
+
* one `next` leaves out is deleted, and the others stay as they are. An array
|
|
89
|
+
* or object with the content it had stays as it is (see {@link sameValue}).
|
|
90
|
+
*
|
|
91
|
+
* @param given - The story's props, a `$state` record of boxes.
|
|
92
|
+
* @param next - The props the story gives now.
|
|
93
|
+
*/
|
|
94
|
+
export function setStoryProps(
|
|
95
|
+
given: GivenProps,
|
|
96
|
+
next: Record<string, unknown>
|
|
97
|
+
): void {
|
|
98
|
+
for (const key of Object.keys(given)) if (!(key in next)) delete given[key];
|
|
99
|
+
for (const [key, value] of Object.entries(next))
|
|
100
|
+
if (!(key in given) || !sameValue(given[key].value, value))
|
|
101
|
+
given[key] = new GivenProp(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** What a story the host sets brings beside its props. */
|
|
105
|
+
export interface StorySettings {
|
|
106
|
+
/** The function props whose calls the pane records. */
|
|
107
|
+
events?: readonly string[];
|
|
108
|
+
/** The two-way props and the events they report through. */
|
|
109
|
+
bindings?: readonly PreviewWriteBack[];
|
|
110
|
+
/** The host's mount of the story, which each recorded event carries. */
|
|
111
|
+
mount?: number;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The Svelte pane's story props as the host's messages set them. A story's
|
|
116
|
+
* events are recorded, and a controlled story takes a two-way prop's new
|
|
117
|
+
* value in the event that reports it, as the React and Vue panes do: the
|
|
118
|
+
* story's prop holds the value the component has taken by the time the
|
|
119
|
+
* host echoes it, and props the host sent before it took the pane's newest
|
|
120
|
+
* write keep that value (`answers`, see {@link PendingWriteBacks}). A new
|
|
121
|
+
* story forgets the last one's writes.
|
|
122
|
+
*
|
|
123
|
+
* @param given - The story's props, a `$state` record of boxes.
|
|
124
|
+
* @param target - The pane's window, which events are reported from.
|
|
125
|
+
* @returns `setStory` for a story the host sets, `setProps` for its props.
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const story = paneProps(given);
|
|
129
|
+
* story.setStory({ value: 'a' }, { bindings: [{ prop: 'value', changeProp: 'onValueChange' }] });
|
|
130
|
+
* story.setProps({ value: 'ab' }, 1);
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export function paneProps(
|
|
134
|
+
given: GivenProps,
|
|
135
|
+
target: Window = window
|
|
136
|
+
): {
|
|
137
|
+
setStory: (props: Record<string, unknown>, settings?: StorySettings) => void;
|
|
138
|
+
setProps: (props: Record<string, unknown>, answers?: number) => void;
|
|
139
|
+
} {
|
|
140
|
+
let settings: StorySettings = {};
|
|
141
|
+
const pending = new PendingWriteBacks();
|
|
142
|
+
function apply(next: Record<string, unknown>): void {
|
|
143
|
+
setStoryProps(
|
|
144
|
+
given,
|
|
145
|
+
withWriteBacks(
|
|
146
|
+
withEventRecorders(
|
|
147
|
+
next,
|
|
148
|
+
settings.events,
|
|
149
|
+
target,
|
|
150
|
+
settings.mount,
|
|
151
|
+
pending
|
|
152
|
+
),
|
|
153
|
+
settings.bindings,
|
|
154
|
+
(prop, value) => {
|
|
155
|
+
pending.wrote(prop, value);
|
|
156
|
+
if (prop in given && !sameValue(given[prop].value, value))
|
|
157
|
+
given[prop] = new GivenProp(value);
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
setStory(props, next = {}) {
|
|
164
|
+
settings = next;
|
|
165
|
+
pending.clear();
|
|
166
|
+
apply(props);
|
|
167
|
+
},
|
|
168
|
+
setProps(props, answers) {
|
|
169
|
+
apply(pending.reconcile(props, answers));
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
// Generic custom-view iframe entry. The host points this iframe at a registered
|
|
2
|
+
// view by `?view=<id>` and drives its globals over the same postMessage handshake
|
|
3
|
+
// the component previews use. The entry loads the kit's setup module for the
|
|
4
|
+
// view's framework (named by `?framework=`, so its stylesheets come first, as
|
|
5
|
+
// in the framework iframes), then the theme and canvas, then the kit's views;
|
|
6
|
+
// it mounts the view under the live theme and options (so a tokens view paints
|
|
7
|
+
// from the kit's real custom properties, with its theme stylesheet in scope).
|
|
8
|
+
// The view stays mounted and hears of each new context - the options the host
|
|
9
|
+
// applies, and the catalog once the handshake delivers it - through the
|
|
10
|
+
// `updates` it mounts with. No view is named here - the kit's views module is
|
|
11
|
+
// the only place a view is registered, so a new view needs no change to this
|
|
12
|
+
// entry or the host.
|
|
13
|
+
import type {
|
|
14
|
+
RenderedView,
|
|
15
|
+
ViewContext,
|
|
16
|
+
ViewFramework,
|
|
17
|
+
} from '../../../src/views';
|
|
18
|
+
import { options as previewOptions } from '@preview-configuration';
|
|
19
|
+
import { optionDefaults } from '../../../src/options';
|
|
20
|
+
import {
|
|
21
|
+
announcePreviewReady,
|
|
22
|
+
announcePreviewShown,
|
|
23
|
+
PREVIEW_MESSAGE,
|
|
24
|
+
PREVIEW_MOUNT_ELEMENT_ID,
|
|
25
|
+
readPreviewMessage,
|
|
26
|
+
reportContentSize,
|
|
27
|
+
selectMessage,
|
|
28
|
+
VIEW_FRAMEWORK_PARAMETER,
|
|
29
|
+
VIEW_PARAMETER,
|
|
30
|
+
} from '../../../src/protocol';
|
|
31
|
+
|
|
32
|
+
const parameters = new URLSearchParams(window.location.search);
|
|
33
|
+
const viewId = parameters.get(VIEW_PARAMETER) ?? '';
|
|
34
|
+
|
|
35
|
+
type SetupFramework = Exclude<ViewFramework, 'dom'>;
|
|
36
|
+
|
|
37
|
+
// Each framework's setup module, loaded only for a view of that framework.
|
|
38
|
+
const SETUP_MODULES: Record<SetupFramework, () => Promise<unknown>> = {
|
|
39
|
+
react: () => import('@preview-setup/react'),
|
|
40
|
+
vue: () => import('@preview-setup/vue'),
|
|
41
|
+
svelte: () => import('@preview-setup/svelte'),
|
|
42
|
+
};
|
|
43
|
+
const loadedSetups = new Set<ViewFramework>(['dom']);
|
|
44
|
+
|
|
45
|
+
async function loadSetup(framework: ViewFramework): Promise<void> {
|
|
46
|
+
if (loadedSetups.has(framework)) return;
|
|
47
|
+
loadedSetups.add(framework);
|
|
48
|
+
await SETUP_MODULES[framework as SetupFramework]();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isSetupFramework(value: string | null): value is SetupFramework {
|
|
52
|
+
return value !== null && Object.hasOwn(SETUP_MODULES, value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The view-side half of the select back-channel: ViewContext exposes this as
|
|
56
|
+
// `selectComponent`, and this iframe relays it to the host as `preview:select` -
|
|
57
|
+
// a view never learns the postMessage vocabulary.
|
|
58
|
+
function selectComponent(id: string, props?: Record<string, unknown>) {
|
|
59
|
+
window.parent?.postMessage(selectMessage(id, props), '*');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let context: ViewContext = {
|
|
63
|
+
options: optionDefaults(previewOptions),
|
|
64
|
+
kitName: '',
|
|
65
|
+
sources: [],
|
|
66
|
+
components: [],
|
|
67
|
+
selectComponent,
|
|
68
|
+
};
|
|
69
|
+
const listeners = new Set<(context: ViewContext) => void>();
|
|
70
|
+
|
|
71
|
+
function update(changes: Partial<ViewContext>): void {
|
|
72
|
+
context = { ...context, ...changes };
|
|
73
|
+
for (const listener of listeners) listener(context);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function start(): Promise<void> {
|
|
77
|
+
const named = parameters.get(VIEW_FRAMEWORK_PARAMETER);
|
|
78
|
+
if (isSetupFramework(named)) await loadSetup(named);
|
|
79
|
+
const { applyGlobals } = await import('../globals');
|
|
80
|
+
const { viewById } = await import('@preview-views');
|
|
81
|
+
const entry = viewById(viewId);
|
|
82
|
+
// A component-tree entry is navigation only (no mount); only a rendered view mounts here.
|
|
83
|
+
const view: RenderedView | undefined =
|
|
84
|
+
entry && !entry.componentTree ? entry : undefined;
|
|
85
|
+
// A page loaded without `?framework=` still gets the view's setup module.
|
|
86
|
+
await loadSetup(view?.framework ?? 'dom');
|
|
87
|
+
const element = document.getElementById(PREVIEW_MOUNT_ELEMENT_ID);
|
|
88
|
+
if (view && element) {
|
|
89
|
+
view.mount(element, context, {
|
|
90
|
+
subscribe(listener) {
|
|
91
|
+
listeners.add(listener);
|
|
92
|
+
return () => {
|
|
93
|
+
listeners.delete(listener);
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
window.addEventListener('message', (event: MessageEvent) => {
|
|
100
|
+
const message = readPreviewMessage(event.data);
|
|
101
|
+
if (!message) return;
|
|
102
|
+
if (message.type === PREVIEW_MESSAGE.globals) {
|
|
103
|
+
applyGlobals(message.options);
|
|
104
|
+
update({ options: message.options ?? {} });
|
|
105
|
+
}
|
|
106
|
+
if (message.type === PREVIEW_MESSAGE.catalog) {
|
|
107
|
+
update({
|
|
108
|
+
kitName: message.kitName ?? '',
|
|
109
|
+
sources: message.sources ?? [],
|
|
110
|
+
components: message.components ?? [],
|
|
111
|
+
});
|
|
112
|
+
// The view with its catalog, once painted, lets the host show the pane.
|
|
113
|
+
announcePreviewShown();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
// Signal readiness so the host replays the current globals (an iframe
|
|
117
|
+
// re-handshakes after each reload), and set the ready flag a test that
|
|
118
|
+
// loads this page on its own waits on.
|
|
119
|
+
announcePreviewReady();
|
|
120
|
+
// The view's size reaches the host on each change, so a shell can size the pane to it.
|
|
121
|
+
if (element) reportContentSize(element);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
void start();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>preview - view</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="app"></div>
|
|
10
|
+
<script type="module" src="./main.ts"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|