@contractspec/lib.surface-runtime 0.2.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 +164 -0
- package/dist/adapters/ai-sdk-stub.d.ts +5 -0
- package/dist/adapters/ai-sdk-stub.js +13 -0
- package/dist/adapters/blocknote-stub.d.ts +6 -0
- package/dist/adapters/blocknote-stub.js +31 -0
- package/dist/adapters/dnd-kit-adapter.d.ts +13 -0
- package/dist/adapters/dnd-kit-adapter.js +44 -0
- package/dist/adapters/dnd-kit-stub.d.ts +6 -0
- package/dist/adapters/dnd-kit-stub.js +8 -0
- package/dist/adapters/floating-ui-stub.d.ts +6 -0
- package/dist/adapters/floating-ui-stub.js +19 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +176 -0
- package/dist/adapters/interfaces.d.ts +75 -0
- package/dist/adapters/interfaces.js +1 -0
- package/dist/adapters/motion-stub.d.ts +7 -0
- package/dist/adapters/motion-stub.js +27 -0
- package/dist/adapters/motion-stub.test.d.ts +1 -0
- package/dist/adapters/resizable-panels-stub.d.ts +6 -0
- package/dist/adapters/resizable-panels-stub.js +46 -0
- package/dist/adapters/resizable-panels-stub.test.d.ts +1 -0
- package/dist/browser/adapters/ai-sdk-stub.js +12 -0
- package/dist/browser/adapters/blocknote-stub.js +30 -0
- package/dist/browser/adapters/dnd-kit-adapter.js +43 -0
- package/dist/browser/adapters/dnd-kit-stub.js +7 -0
- package/dist/browser/adapters/floating-ui-stub.js +18 -0
- package/dist/browser/adapters/index.js +175 -0
- package/dist/browser/adapters/interfaces.js +0 -0
- package/dist/browser/adapters/motion-stub.js +26 -0
- package/dist/browser/adapters/resizable-panels-stub.js +45 -0
- package/dist/browser/evals/golden-context.js +0 -0
- package/dist/browser/evals/golden-harness.js +848 -0
- package/dist/browser/examples/pm-workbench.bundle.js +476 -0
- package/dist/browser/i18n/catalogs/en.js +71 -0
- package/dist/browser/i18n/catalogs/es.js +32 -0
- package/dist/browser/i18n/catalogs/fr.js +32 -0
- package/dist/browser/i18n/catalogs/index.js +133 -0
- package/dist/browser/i18n/index.js +173 -0
- package/dist/browser/i18n/keys.js +19 -0
- package/dist/browser/i18n/messages.js +143 -0
- package/dist/browser/index.js +2466 -0
- package/dist/browser/react/BundleProvider.js +47 -0
- package/dist/browser/react/BundleRenderer.js +726 -0
- package/dist/browser/react/OverlayConflictResolver.js +255 -0
- package/dist/browser/react/PatchProposalCard.js +255 -0
- package/dist/browser/react/RegionRenderer.js +128 -0
- package/dist/browser/react/SlotRenderer.js +118 -0
- package/dist/browser/react/WidgetPalette.js +59 -0
- package/dist/browser/react/index.js +792 -0
- package/dist/browser/runtime/apply-surface-patch.js +322 -0
- package/dist/browser/runtime/audit-events.js +137 -0
- package/dist/browser/runtime/build-context.js +55 -0
- package/dist/browser/runtime/extension-registry.js +58 -0
- package/dist/browser/runtime/field-renderer-registry.js +145 -0
- package/dist/browser/runtime/index.js +1496 -0
- package/dist/browser/runtime/overlay-alignment.js +83 -0
- package/dist/browser/runtime/overlay-signer.js +15 -0
- package/dist/browser/runtime/override-store.js +52 -0
- package/dist/browser/runtime/planner-prompt.js +67 -0
- package/dist/browser/runtime/planner-tools.js +77 -0
- package/dist/browser/runtime/policy-eval.js +155 -0
- package/dist/browser/runtime/preference-adapter.js +67 -0
- package/dist/browser/runtime/resolve-bundle.js +767 -0
- package/dist/browser/runtime/resolve-preferences.js +59 -0
- package/dist/browser/runtime/rollback.js +347 -0
- package/dist/browser/runtime/widget-registry.js +36 -0
- package/dist/browser/spec/define-module-bundle.js +113 -0
- package/dist/browser/spec/index.js +319 -0
- package/dist/browser/spec/types.js +0 -0
- package/dist/browser/spec/validate-bundle.js +65 -0
- package/dist/browser/spec/validate-surface-patch.js +206 -0
- package/dist/browser/spec/verification-snapshot-types.js +0 -0
- package/dist/browser/telemetry/index.js +20 -0
- package/dist/browser/telemetry/surface-metrics.js +20 -0
- package/dist/evals/golden-context.d.ts +24 -0
- package/dist/evals/golden-context.js +1 -0
- package/dist/evals/golden-harness.d.ts +29 -0
- package/dist/evals/golden-harness.js +849 -0
- package/dist/evals/golden-harness.test.d.ts +1 -0
- package/dist/examples/pm-workbench.bundle.d.ts +177 -0
- package/dist/examples/pm-workbench.bundle.js +477 -0
- package/dist/i18n/catalogs/en.d.ts +1 -0
- package/dist/i18n/catalogs/en.js +72 -0
- package/dist/i18n/catalogs/es.d.ts +1 -0
- package/dist/i18n/catalogs/es.js +33 -0
- package/dist/i18n/catalogs/fr.d.ts +1 -0
- package/dist/i18n/catalogs/fr.js +33 -0
- package/dist/i18n/catalogs/index.d.ts +3 -0
- package/dist/i18n/catalogs/index.js +134 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.js +174 -0
- package/dist/i18n/keys.d.ts +20 -0
- package/dist/i18n/keys.js +20 -0
- package/dist/i18n/messages.d.ts +5 -0
- package/dist/i18n/messages.js +144 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +2467 -0
- package/dist/node/adapters/ai-sdk-stub.js +12 -0
- package/dist/node/adapters/blocknote-stub.js +30 -0
- package/dist/node/adapters/dnd-kit-adapter.js +43 -0
- package/dist/node/adapters/dnd-kit-stub.js +7 -0
- package/dist/node/adapters/floating-ui-stub.js +18 -0
- package/dist/node/adapters/index.js +175 -0
- package/dist/node/adapters/interfaces.js +0 -0
- package/dist/node/adapters/motion-stub.js +26 -0
- package/dist/node/adapters/resizable-panels-stub.js +45 -0
- package/dist/node/evals/golden-context.js +0 -0
- package/dist/node/evals/golden-harness.js +848 -0
- package/dist/node/examples/pm-workbench.bundle.js +476 -0
- package/dist/node/i18n/catalogs/en.js +71 -0
- package/dist/node/i18n/catalogs/es.js +32 -0
- package/dist/node/i18n/catalogs/fr.js +32 -0
- package/dist/node/i18n/catalogs/index.js +133 -0
- package/dist/node/i18n/index.js +173 -0
- package/dist/node/i18n/keys.js +19 -0
- package/dist/node/i18n/messages.js +143 -0
- package/dist/node/index.js +2466 -0
- package/dist/node/react/BundleProvider.js +47 -0
- package/dist/node/react/BundleRenderer.js +726 -0
- package/dist/node/react/OverlayConflictResolver.js +255 -0
- package/dist/node/react/PatchProposalCard.js +255 -0
- package/dist/node/react/RegionRenderer.js +128 -0
- package/dist/node/react/SlotRenderer.js +118 -0
- package/dist/node/react/WidgetPalette.js +59 -0
- package/dist/node/react/index.js +792 -0
- package/dist/node/runtime/apply-surface-patch.js +322 -0
- package/dist/node/runtime/audit-events.js +137 -0
- package/dist/node/runtime/build-context.js +55 -0
- package/dist/node/runtime/extension-registry.js +58 -0
- package/dist/node/runtime/field-renderer-registry.js +145 -0
- package/dist/node/runtime/index.js +1496 -0
- package/dist/node/runtime/overlay-alignment.js +83 -0
- package/dist/node/runtime/overlay-signer.js +15 -0
- package/dist/node/runtime/override-store.js +52 -0
- package/dist/node/runtime/planner-prompt.js +67 -0
- package/dist/node/runtime/planner-tools.js +77 -0
- package/dist/node/runtime/policy-eval.js +155 -0
- package/dist/node/runtime/preference-adapter.js +67 -0
- package/dist/node/runtime/resolve-bundle.js +767 -0
- package/dist/node/runtime/resolve-preferences.js +59 -0
- package/dist/node/runtime/rollback.js +347 -0
- package/dist/node/runtime/widget-registry.js +36 -0
- package/dist/node/spec/define-module-bundle.js +113 -0
- package/dist/node/spec/index.js +319 -0
- package/dist/node/spec/types.js +0 -0
- package/dist/node/spec/validate-bundle.js +65 -0
- package/dist/node/spec/validate-surface-patch.js +206 -0
- package/dist/node/spec/verification-snapshot-types.js +0 -0
- package/dist/node/telemetry/index.js +20 -0
- package/dist/node/telemetry/surface-metrics.js +20 -0
- package/dist/react/BundleProvider.d.ts +13 -0
- package/dist/react/BundleProvider.js +48 -0
- package/dist/react/BundleRenderer.d.ts +22 -0
- package/dist/react/BundleRenderer.js +727 -0
- package/dist/react/OverlayConflictResolver.d.ts +15 -0
- package/dist/react/OverlayConflictResolver.js +256 -0
- package/dist/react/PatchProposalCard.d.ts +13 -0
- package/dist/react/PatchProposalCard.js +256 -0
- package/dist/react/RegionRenderer.d.ts +13 -0
- package/dist/react/RegionRenderer.js +129 -0
- package/dist/react/SlotRenderer.d.ts +13 -0
- package/dist/react/SlotRenderer.js +119 -0
- package/dist/react/WidgetPalette.d.ts +12 -0
- package/dist/react/WidgetPalette.js +60 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +793 -0
- package/dist/runtime/apply-surface-patch.d.ts +15 -0
- package/dist/runtime/apply-surface-patch.js +323 -0
- package/dist/runtime/apply-surface-patch.test.d.ts +1 -0
- package/dist/runtime/audit-events.d.ts +70 -0
- package/dist/runtime/audit-events.js +138 -0
- package/dist/runtime/audit-events.test.d.ts +1 -0
- package/dist/runtime/build-context.d.ts +9 -0
- package/dist/runtime/build-context.js +56 -0
- package/dist/runtime/extension-registry.d.ts +39 -0
- package/dist/runtime/extension-registry.js +59 -0
- package/dist/runtime/field-renderer-registry.d.ts +23 -0
- package/dist/runtime/field-renderer-registry.js +146 -0
- package/dist/runtime/field-renderer-registry.test.d.ts +1 -0
- package/dist/runtime/index.d.ts +16 -0
- package/dist/runtime/index.js +1497 -0
- package/dist/runtime/overlay-alignment.d.ts +49 -0
- package/dist/runtime/overlay-alignment.js +84 -0
- package/dist/runtime/overlay-alignment.test.d.ts +1 -0
- package/dist/runtime/overlay-signer.d.ts +15 -0
- package/dist/runtime/overlay-signer.js +16 -0
- package/dist/runtime/override-store.d.ts +44 -0
- package/dist/runtime/override-store.js +53 -0
- package/dist/runtime/override-store.test.d.ts +1 -0
- package/dist/runtime/planner-prompt.d.ts +39 -0
- package/dist/runtime/planner-prompt.js +68 -0
- package/dist/runtime/planner-prompt.test.d.ts +1 -0
- package/dist/runtime/planner-tools.d.ts +106 -0
- package/dist/runtime/planner-tools.js +78 -0
- package/dist/runtime/planner-tools.test.d.ts +1 -0
- package/dist/runtime/policy-eval.d.ts +23 -0
- package/dist/runtime/policy-eval.js +156 -0
- package/dist/runtime/preference-adapter.d.ts +6 -0
- package/dist/runtime/preference-adapter.js +68 -0
- package/dist/runtime/resolve-bundle.d.ts +68 -0
- package/dist/runtime/resolve-bundle.js +768 -0
- package/dist/runtime/resolve-bundle.test.d.ts +1 -0
- package/dist/runtime/resolve-preferences.d.ts +9 -0
- package/dist/runtime/resolve-preferences.js +60 -0
- package/dist/runtime/resolve-preferences.test.d.ts +1 -0
- package/dist/runtime/rollback.d.ts +21 -0
- package/dist/runtime/rollback.js +348 -0
- package/dist/runtime/rollback.test.d.ts +1 -0
- package/dist/runtime/widget-registry.d.ts +26 -0
- package/dist/runtime/widget-registry.js +37 -0
- package/dist/runtime/widget-registry.test.d.ts +1 -0
- package/dist/spec/define-module-bundle.d.ts +17 -0
- package/dist/spec/define-module-bundle.js +114 -0
- package/dist/spec/define-module-bundle.test.d.ts +1 -0
- package/dist/spec/index.d.ts +5 -0
- package/dist/spec/index.js +320 -0
- package/dist/spec/types.d.ts +494 -0
- package/dist/spec/types.js +1 -0
- package/dist/spec/validate-bundle.d.ts +23 -0
- package/dist/spec/validate-bundle.js +66 -0
- package/dist/spec/validate-bundle.test.d.ts +1 -0
- package/dist/spec/validate-surface-patch.d.ts +39 -0
- package/dist/spec/validate-surface-patch.js +207 -0
- package/dist/spec/validate-surface-patch.test.d.ts +1 -0
- package/dist/spec/verification-snapshot-types.d.ts +23 -0
- package/dist/spec/verification-snapshot-types.js +1 -0
- package/dist/spec/verification-snapshot.test.d.ts +5 -0
- package/dist/telemetry/index.d.ts +5 -0
- package/dist/telemetry/index.js +21 -0
- package/dist/telemetry/surface-metrics.d.ts +17 -0
- package/dist/telemetry/surface-metrics.js +21 -0
- package/package.json +920 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# @contractspec/lib.surface-runtime
|
|
2
|
+
|
|
3
|
+
AI-native surface specs and web runtime for adaptive ContractSpec surfaces.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This library provides:
|
|
8
|
+
|
|
9
|
+
- **ModuleBundleSpec** — TypeScript types for defining surface contracts (routes, slots, layouts, data recipes)
|
|
10
|
+
- **defineModuleBundle** — Runtime validator for bundle specs
|
|
11
|
+
- **resolveBundle** — Resolves a bundle spec + context into a ResolvedSurfacePlan
|
|
12
|
+
- **resolvePreferenceProfile** — Resolves preferences by scope order (user → … → session)
|
|
13
|
+
- **defaultPreferenceAdapter** — Stub `BundlePreferenceAdapter` (resolve + savePreferencePatch)
|
|
14
|
+
- **applySurfacePatch** — Applies patch operations to a plan
|
|
15
|
+
- **BundleProvider** / **BundleRenderer** — React components for rendering resolved plans
|
|
16
|
+
- **RegionRenderer** / **SlotRenderer** — Layout tree and slot content rendering
|
|
17
|
+
- **Adapters** — BlockNote, dnd-kit, Floating UI, Motion, resizable-panels, AI SDK (stubs)
|
|
18
|
+
|
|
19
|
+
## Terminology (Glossary)
|
|
20
|
+
|
|
21
|
+
- **Bundle spec** / **Surface spec** (ModuleBundleSpec): A contract type defining a surface (routes, slots, layouts). Not an architectural layer. Use "surface spec" in prose to avoid confusion.
|
|
22
|
+
- **Bundle** (architectural): A domain package in `packages/bundles/` (e.g. bundle.workspace). Contains business logic.
|
|
23
|
+
|
|
24
|
+
### Preference vs layout vs view vs overlay
|
|
25
|
+
|
|
26
|
+
| Concept | Meaning | Storage |
|
|
27
|
+
|---------|---------|---------|
|
|
28
|
+
| **Preference** | Stable user intent (e.g. `density=dense`, `guidance=hints`). Orthogonal 7-dimension model. | User/workspace/surface scope via `BundlePreferenceAdapter`. |
|
|
29
|
+
| **Layout snapshot** | Concrete arrangement (panel sizes, tab order, collapsed state). | Distinct from preferences; use `persistKey` + overlay-engine for durable layout. |
|
|
30
|
+
| **View** | Data-shaping projection (filters, sort, visible columns). | Often session-scoped or view-state. |
|
|
31
|
+
| **Overlay** | Durable mutation to the rendered contract (patches, field visibility). | `lib.overlay-engine` (OverlaySpec, merger). |
|
|
32
|
+
|
|
33
|
+
Do not merge these into one blob. Preferences drive adaptation; layout snapshots persist geometry; overlays persist structural patches.
|
|
34
|
+
|
|
35
|
+
### Preference scope resolution order
|
|
36
|
+
|
|
37
|
+
Preferences are merged in this order (later overrides earlier):
|
|
38
|
+
|
|
39
|
+
1. **user** — Global user defaults
|
|
40
|
+
2. **workspace-user** — Workspace-specific user defaults
|
|
41
|
+
3. **bundle** — Bundle-level defaults
|
|
42
|
+
4. **surface** — Surface-specific overrides
|
|
43
|
+
5. **entity** — Entity-context overrides
|
|
44
|
+
6. **session** — Session overrides (highest priority)
|
|
45
|
+
|
|
46
|
+
### Dimension → runtime behavior (summary)
|
|
47
|
+
|
|
48
|
+
| Dimension | Runtime effect |
|
|
49
|
+
|-----------|----------------|
|
|
50
|
+
| **guidance** | Hints, onboarding, progressive disclosure, assistant prompting style |
|
|
51
|
+
| **density** | Spacing, panel expansion, table vs card layout, side rails |
|
|
52
|
+
| **dataDepth** | Query shape, pagination size, inline expansion, raw payload access |
|
|
53
|
+
| **control** | Visible toggles, advanced inspectors, batch ops, policy editor |
|
|
54
|
+
| **media** | Text vs visual vs voice surfaces, charts vs prose, TTS |
|
|
55
|
+
| **pace** | Animation duration, confirmation depth, skeleton persistence; respects `prefers-reduced-motion` |
|
|
56
|
+
| **narrative** | Order of summary vs evidence, top-down vs bottom-up presentation |
|
|
57
|
+
|
|
58
|
+
Every surface must declare `verification.dimensions` describing how it responds to each dimension. See [docs/verification-matrix.md](./docs/verification-matrix.md) for the verification matrix, review questions, and snapshot coverage.
|
|
59
|
+
|
|
60
|
+
## Package Strategy
|
|
61
|
+
|
|
62
|
+
### Hard dependencies
|
|
63
|
+
|
|
64
|
+
- `@contractspec/lib.contracts-spec` — operations, capabilities, policies, presentations
|
|
65
|
+
- `@contractspec/lib.overlay-engine` — typed, auditable overrides
|
|
66
|
+
- `@contractspec/lib.observability` — tracing, metrics, lifecycle instrumentation
|
|
67
|
+
- `zod` — schema validation
|
|
68
|
+
|
|
69
|
+
### Interoperability (consumes or composes with)
|
|
70
|
+
|
|
71
|
+
- `@contractspec/lib.contracts-runtime-client-react` — feature/form rendering
|
|
72
|
+
- `@contractspec/lib.presentation-runtime-react` — workflow, list state
|
|
73
|
+
- `@contractspec/lib.ai-agent` — agent orchestration (when assistant integration is added)
|
|
74
|
+
- `@contractspec/lib.ai-providers` — planner model selection
|
|
75
|
+
- `@contractspec/lib.metering` — when AI/chat features are used
|
|
76
|
+
- `@contractspec/lib.personalization` — preference dimensions (or surface-runtime-owned until adopted)
|
|
77
|
+
|
|
78
|
+
### Peer dependencies
|
|
79
|
+
|
|
80
|
+
- `react`, `react-dom` — optional; required only for React renderer
|
|
81
|
+
- UI libs (BlockNote, dnd-kit, Floating UI, Motion, resizable-panels) — optional; added as peers when adapters are implemented
|
|
82
|
+
|
|
83
|
+
## Usage
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { defineModuleBundle } from "@contractspec/lib.surface-runtime/spec";
|
|
87
|
+
import { resolveBundle } from "@contractspec/lib.surface-runtime/runtime";
|
|
88
|
+
|
|
89
|
+
const PmWorkbenchBundle = defineModuleBundle({
|
|
90
|
+
meta: { key: "pm.workbench", version: "0.1.0", title: "PM Workbench" },
|
|
91
|
+
routes: [{ routeId: "pm-issue", path: "/operate/pm/issues/:issueId", defaultSurface: "issue-workbench" }],
|
|
92
|
+
surfaces: {
|
|
93
|
+
"issue-workbench": {
|
|
94
|
+
surfaceId: "issue-workbench",
|
|
95
|
+
kind: "workbench",
|
|
96
|
+
title: "Issue Workbench",
|
|
97
|
+
slots: [...],
|
|
98
|
+
layouts: [...],
|
|
99
|
+
data: [],
|
|
100
|
+
verification: { dimensions: { guidance: "...", density: "...", ... } },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const plan = await resolveBundle(PmWorkbenchBundle, ctx);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Dependencies
|
|
109
|
+
|
|
110
|
+
See [Package Strategy](#package-strategy) above. Core: contracts-spec, overlay-engine, observability, zod.
|
|
111
|
+
|
|
112
|
+
## AI Chat Integration (07_ai_native_chat_and_generative_ui)
|
|
113
|
+
|
|
114
|
+
The assistant slot can render chat UI from `@contractspec/module.ai-chat`. Wire as follows:
|
|
115
|
+
|
|
116
|
+
1. **Assistant slot**: Declare a slot with `role: 'assistant'` in the surface spec. Set `SurfaceAiSpec.assistantSlotId` to that slot's `slotId`.
|
|
117
|
+
|
|
118
|
+
2. **BundleRenderer + assistant slot**: When `SlotRenderer` receives `slotId === assistantSlotId`, the bundle should render `ChatContainer` from `@contractspec/module.ai-chat` instead of default slot content. Pass `useChat` from the same module for thread state.
|
|
119
|
+
|
|
120
|
+
3. **Provider config**: Use `createProvider` from `@contractspec/lib.ai-providers` to build the model config. Pass it to the planner (ContractSpecAgent) and to the chat UI (useChat options).
|
|
121
|
+
|
|
122
|
+
4. **Planner integration**: Use `compilePlannerPrompt` and `proposePatchToolConfig` from this package. Wire the propose-patch tool handler to `validatePatchProposal` before returning. Map planner output to `PlannerResponse` (summary, intent, patchProposals, blockDrafts).
|
|
123
|
+
|
|
124
|
+
5. **Patch approval flow**: On accept, emit `patch.approved` audit event, call `applySurfacePatch`, then promote to session overlay. On reject, emit `patch.rejected`. Use `PatchApprovalHandler` for the callback signature.
|
|
125
|
+
|
|
126
|
+
## Policy, Audit, and Rollback (10_policy_audit_and_safety)
|
|
127
|
+
|
|
128
|
+
### Policy hooks
|
|
129
|
+
|
|
130
|
+
Pass `policy` and `audit` to `resolveBundle` options:
|
|
131
|
+
|
|
132
|
+
- **evaluateNode**: Return `UiPolicyDecision` (allow/deny/redact) per node. Stub: allow all.
|
|
133
|
+
- **redactBinding**: Redact binding values. Stub: pass-through.
|
|
134
|
+
- **evaluatePatchProposal**: Return allow/deny/require-approval for patch ops. Stub: allow.
|
|
135
|
+
|
|
136
|
+
Use `evaluateAndEmitPatchPolicy` before applying patches to gate on policy and emit `policy.denied` when blocked.
|
|
137
|
+
|
|
138
|
+
### Audit events
|
|
139
|
+
|
|
140
|
+
Implement `BundleAuditEmitter` and pass to `resolveBundle` and patch approval flow. Events: `surface.resolved`, `patch.proposed`, `patch.approved`, `patch.rejected`, `overlay.saved`, `overlay.applied`, `overlay.failed`, `policy.denied`, `policy.redacted`. Use `emitPatchProposed`, `emitPatchApproved`, etc. from `./runtime`.
|
|
141
|
+
|
|
142
|
+
### Rollback
|
|
143
|
+
|
|
144
|
+
`applySurfacePatch` returns `inverseOps` for reversible ops (insert/remove, replace, set-layout, reveal/hide-field). Store `OverlayApprovalMeta` (forwardOps, inverseOps, actorId, approvedAt) with each approved overlay. Use `rollbackSurfacePatches(plan, approvalStack, count)` to revert last N patches.
|
|
145
|
+
|
|
146
|
+
**Rollback limits**: move-node, resize-panel, set-focus, promote-action require previous state for full inverse; currently return null inverse. Document when storing approval metadata.
|
|
147
|
+
|
|
148
|
+
## Rollout Status
|
|
149
|
+
|
|
150
|
+
See [ROLLOUT.md](./ROLLOUT.md) for the phased rollout plan, performance budgets, risks, and pilot route (`/operate/pm/issues/:issueId`).
|
|
151
|
+
|
|
152
|
+
**Current:** Phase 0–2 complete (scaffold, resolver, renderer). Phase 3–7 pending.
|
|
153
|
+
|
|
154
|
+
## Adapter Rule (Lint)
|
|
155
|
+
|
|
156
|
+
**Rule**: No direct third-party UI imports outside `src/adapters/`. BlockNote, dnd-kit, Floating UI, Motion, resizable-panels, AI SDK UI—all must be behind adapter boundaries.
|
|
157
|
+
|
|
158
|
+
**Enforcement**: Run `bun run lint:adapters` to detect violations. Integrate into CI if desired.
|
|
159
|
+
|
|
160
|
+
## Phase 2 Status
|
|
161
|
+
|
|
162
|
+
Types, defineModuleBundle, resolveBundle, applySurfacePatch, BundleProvider, BundleRenderer, RegionRenderer, SlotRenderer, and adapter stubs are implemented. ResolvedSurfacePlan includes layoutRoot for rendering. Motion tokens map pace to duration/entrance. Panel persistence (persistKey) via localStorage. Full BlockNote/dnd-kit/Floating UI integrations deferred to later phases. Planner prompt compiler, propose-patch tool schema, and patch proposal validator added for AI chat integration.
|
|
163
|
+
|
|
164
|
+
**Observability (09):** Tracing (`traceAsync`), metrics (`resolution_duration_ms`, patch acceptance/rejection, policy denial, surface fallback, missing renderer), structured logging. Golden-context harness for resolver evals with snapshot tests. Performance budget: resolver p95 <100ms (server). See [docs/evals-runbook.md](./docs/evals-runbook.md) and `./telemetry`.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/adapters/blocknote-stub.tsx
|
|
3
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
4
|
+
var blocknoteAdapterStub = {
|
|
5
|
+
supportsNode(_kind) {
|
|
6
|
+
return false;
|
|
7
|
+
},
|
|
8
|
+
createSchema(_registry) {
|
|
9
|
+
return {};
|
|
10
|
+
},
|
|
11
|
+
renderNode(node, _ctx) {
|
|
12
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
13
|
+
"data-blocknote-stub": true,
|
|
14
|
+
"data-node-id": node.nodeId,
|
|
15
|
+
children: node.title ?? node.kind
|
|
16
|
+
}, undefined, false, undefined, this);
|
|
17
|
+
},
|
|
18
|
+
async serialize(node) {
|
|
19
|
+
return { nodeId: node.nodeId, kind: node.kind };
|
|
20
|
+
},
|
|
21
|
+
async deserialize(input) {
|
|
22
|
+
const o = input;
|
|
23
|
+
return {
|
|
24
|
+
nodeId: o?.nodeId ?? "stub",
|
|
25
|
+
kind: o?.kind ?? "custom-widget"
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export {
|
|
30
|
+
blocknoteAdapterStub
|
|
31
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dnd-kit adapter for customization mode. Wraps content in DndContext.
|
|
3
|
+
* Requires @dnd-kit/core (optional peer). Falls back to stub when not installed.
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import type { SurfacePatchOp } from '../spec/types';
|
|
7
|
+
import type { DragDropBundleAdapter } from './interfaces';
|
|
8
|
+
export interface DndWrapperProps {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
mutableSlots: string[];
|
|
11
|
+
onPatch: (ops: SurfacePatchOp[]) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const dndKitAdapter: DragDropBundleAdapter;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/adapters/dnd-kit-adapter.tsx
|
|
3
|
+
import {
|
|
4
|
+
DndContext,
|
|
5
|
+
PointerSensor,
|
|
6
|
+
useSensor,
|
|
7
|
+
useSensors
|
|
8
|
+
} from "@dnd-kit/core";
|
|
9
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
10
|
+
"use client";
|
|
11
|
+
var noop = () => {
|
|
12
|
+
return;
|
|
13
|
+
};
|
|
14
|
+
var _onPatch = noop;
|
|
15
|
+
function handleDragEnd(event) {
|
|
16
|
+
const { active, over } = event;
|
|
17
|
+
if (!over)
|
|
18
|
+
return;
|
|
19
|
+
const fromId = active.id;
|
|
20
|
+
const toSlotId = over.id;
|
|
21
|
+
if (fromId && toSlotId) {
|
|
22
|
+
_onPatch([{ op: "move-node", nodeId: fromId, toSlotId }]);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function DndWrapperComponent({ children, onPatch }) {
|
|
26
|
+
_onPatch = onPatch;
|
|
27
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
28
|
+
activationConstraint: { distance: 8 }
|
|
29
|
+
}));
|
|
30
|
+
return /* @__PURE__ */ jsxDEV(DndContext, {
|
|
31
|
+
sensors,
|
|
32
|
+
onDragEnd: handleDragEnd,
|
|
33
|
+
children
|
|
34
|
+
}, undefined, false, undefined, this);
|
|
35
|
+
}
|
|
36
|
+
var dndKitAdapter = {
|
|
37
|
+
enableSurfaceEditing(args) {
|
|
38
|
+
_onPatch = args.onPatch;
|
|
39
|
+
},
|
|
40
|
+
DndWrapper: DndWrapperComponent
|
|
41
|
+
};
|
|
42
|
+
export {
|
|
43
|
+
dndKitAdapter
|
|
44
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/adapters/floating-ui-stub.tsx
|
|
3
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
4
|
+
var floatingUiAdapterStub = {
|
|
5
|
+
renderAnchoredMenu({ anchorId, items }) {
|
|
6
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
7
|
+
"data-floating-stub": true,
|
|
8
|
+
"data-anchor-id": anchorId,
|
|
9
|
+
role: "menu",
|
|
10
|
+
children: items.map((a) => /* @__PURE__ */ jsxDEV("div", {
|
|
11
|
+
role: "menuitem",
|
|
12
|
+
children: a.title
|
|
13
|
+
}, a.actionId, false, undefined, this))
|
|
14
|
+
}, undefined, false, undefined, this);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export {
|
|
18
|
+
floatingUiAdapterStub
|
|
19
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI library adapters. No direct third-party imports outside this directory.
|
|
3
|
+
*/
|
|
4
|
+
export type { AiSdkBundleAdapter, BlockNoteBundleAdapter, DragDropBundleAdapter, FieldRendererRegistry, FloatingBundleAdapter, MotionBundleAdapter, MotionTokens, PanelLayoutAdapter, RenderContext, RenderRegionFn, } from './interfaces';
|
|
5
|
+
export { aiSdkAdapterStub } from './ai-sdk-stub';
|
|
6
|
+
export { blocknoteAdapterStub } from './blocknote-stub';
|
|
7
|
+
export { dndKitAdapterStub } from './dnd-kit-stub';
|
|
8
|
+
export { dndKitAdapter } from './dnd-kit-adapter';
|
|
9
|
+
export { floatingUiAdapterStub } from './floating-ui-stub';
|
|
10
|
+
export { motionAdapterStub } from './motion-stub';
|
|
11
|
+
export { resizablePanelsAdapterStub } from './resizable-panels-stub';
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/adapters/ai-sdk-stub.ts
|
|
3
|
+
var aiSdkAdapterStub = {
|
|
4
|
+
startThread(_args) {
|
|
5
|
+
return null;
|
|
6
|
+
},
|
|
7
|
+
async requestPatches(_args) {
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/adapters/blocknote-stub.tsx
|
|
13
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
14
|
+
var blocknoteAdapterStub = {
|
|
15
|
+
supportsNode(_kind) {
|
|
16
|
+
return false;
|
|
17
|
+
},
|
|
18
|
+
createSchema(_registry) {
|
|
19
|
+
return {};
|
|
20
|
+
},
|
|
21
|
+
renderNode(node, _ctx) {
|
|
22
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
23
|
+
"data-blocknote-stub": true,
|
|
24
|
+
"data-node-id": node.nodeId,
|
|
25
|
+
children: node.title ?? node.kind
|
|
26
|
+
}, undefined, false, undefined, this);
|
|
27
|
+
},
|
|
28
|
+
async serialize(node) {
|
|
29
|
+
return { nodeId: node.nodeId, kind: node.kind };
|
|
30
|
+
},
|
|
31
|
+
async deserialize(input) {
|
|
32
|
+
const o = input;
|
|
33
|
+
return {
|
|
34
|
+
nodeId: o?.nodeId ?? "stub",
|
|
35
|
+
kind: o?.kind ?? "custom-widget"
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/adapters/dnd-kit-adapter.tsx
|
|
41
|
+
import {
|
|
42
|
+
DndContext,
|
|
43
|
+
PointerSensor,
|
|
44
|
+
useSensor,
|
|
45
|
+
useSensors
|
|
46
|
+
} from "@dnd-kit/core";
|
|
47
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
48
|
+
"use client";
|
|
49
|
+
var noop = () => {
|
|
50
|
+
return;
|
|
51
|
+
};
|
|
52
|
+
var _onPatch = noop;
|
|
53
|
+
function handleDragEnd(event) {
|
|
54
|
+
const { active, over } = event;
|
|
55
|
+
if (!over)
|
|
56
|
+
return;
|
|
57
|
+
const fromId = active.id;
|
|
58
|
+
const toSlotId = over.id;
|
|
59
|
+
if (fromId && toSlotId) {
|
|
60
|
+
_onPatch([{ op: "move-node", nodeId: fromId, toSlotId }]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function DndWrapperComponent({ children, onPatch }) {
|
|
64
|
+
_onPatch = onPatch;
|
|
65
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
66
|
+
activationConstraint: { distance: 8 }
|
|
67
|
+
}));
|
|
68
|
+
return /* @__PURE__ */ jsxDEV2(DndContext, {
|
|
69
|
+
sensors,
|
|
70
|
+
onDragEnd: handleDragEnd,
|
|
71
|
+
children
|
|
72
|
+
}, undefined, false, undefined, this);
|
|
73
|
+
}
|
|
74
|
+
var dndKitAdapter = {
|
|
75
|
+
enableSurfaceEditing(args) {
|
|
76
|
+
_onPatch = args.onPatch;
|
|
77
|
+
},
|
|
78
|
+
DndWrapper: DndWrapperComponent
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// src/adapters/dnd-kit-stub.ts
|
|
82
|
+
var dndKitAdapterStub = {
|
|
83
|
+
enableSurfaceEditing(_args) {}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/adapters/floating-ui-stub.tsx
|
|
87
|
+
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
88
|
+
var floatingUiAdapterStub = {
|
|
89
|
+
renderAnchoredMenu({ anchorId, items }) {
|
|
90
|
+
return /* @__PURE__ */ jsxDEV3("div", {
|
|
91
|
+
"data-floating-stub": true,
|
|
92
|
+
"data-anchor-id": anchorId,
|
|
93
|
+
role: "menu",
|
|
94
|
+
children: items.map((a) => /* @__PURE__ */ jsxDEV3("div", {
|
|
95
|
+
role: "menuitem",
|
|
96
|
+
children: a.title
|
|
97
|
+
}, a.actionId, false, undefined, this))
|
|
98
|
+
}, undefined, false, undefined, this);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// src/adapters/motion-stub.ts
|
|
103
|
+
var PACE_TOKENS = {
|
|
104
|
+
deliberate: {
|
|
105
|
+
durationMs: 300,
|
|
106
|
+
enableEntrance: true,
|
|
107
|
+
layout: true
|
|
108
|
+
},
|
|
109
|
+
balanced: {
|
|
110
|
+
durationMs: 150,
|
|
111
|
+
enableEntrance: true,
|
|
112
|
+
layout: true
|
|
113
|
+
},
|
|
114
|
+
rapid: {
|
|
115
|
+
durationMs: 50,
|
|
116
|
+
enableEntrance: false,
|
|
117
|
+
layout: false
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var motionAdapterStub = {
|
|
121
|
+
getTokens(pace) {
|
|
122
|
+
return PACE_TOKENS[pace] ?? PACE_TOKENS.balanced;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/adapters/resizable-panels-stub.tsx
|
|
127
|
+
import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
|
|
128
|
+
var LAYOUT_STORAGE_KEY = "surface-runtime:panel-layout:";
|
|
129
|
+
var resizablePanelsAdapterStub = {
|
|
130
|
+
renderPanelGroup(region, ctx, renderChild) {
|
|
131
|
+
const direction = region.direction === "horizontal" ? "row" : "column";
|
|
132
|
+
return /* @__PURE__ */ jsxDEV4("div", {
|
|
133
|
+
"data-panel-group": true,
|
|
134
|
+
"data-persist-key": region.persistKey,
|
|
135
|
+
style: {
|
|
136
|
+
display: "flex",
|
|
137
|
+
flexDirection: direction,
|
|
138
|
+
flex: 1,
|
|
139
|
+
minHeight: 0
|
|
140
|
+
},
|
|
141
|
+
children: region.children.map((child, i) => /* @__PURE__ */ jsxDEV4("div", {
|
|
142
|
+
style: { flex: 1, minWidth: 0, minHeight: 0 },
|
|
143
|
+
children: renderChild(child, ctx)
|
|
144
|
+
}, i, false, undefined, this))
|
|
145
|
+
}, undefined, false, undefined, this);
|
|
146
|
+
},
|
|
147
|
+
async restoreLayout(persistKey) {
|
|
148
|
+
if (typeof localStorage === "undefined")
|
|
149
|
+
return null;
|
|
150
|
+
try {
|
|
151
|
+
const raw = localStorage.getItem(LAYOUT_STORAGE_KEY + persistKey);
|
|
152
|
+
if (!raw)
|
|
153
|
+
return null;
|
|
154
|
+
const parsed = JSON.parse(raw);
|
|
155
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
async saveLayout(persistKey, sizes) {
|
|
161
|
+
if (typeof localStorage === "undefined")
|
|
162
|
+
return;
|
|
163
|
+
try {
|
|
164
|
+
localStorage.setItem(LAYOUT_STORAGE_KEY + persistKey, JSON.stringify(sizes));
|
|
165
|
+
} catch {}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
export {
|
|
169
|
+
resizablePanelsAdapterStub,
|
|
170
|
+
motionAdapterStub,
|
|
171
|
+
floatingUiAdapterStub,
|
|
172
|
+
dndKitAdapterStub,
|
|
173
|
+
dndKitAdapter,
|
|
174
|
+
blocknoteAdapterStub,
|
|
175
|
+
aiSdkAdapterStub
|
|
176
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter interfaces for third-party UI libraries.
|
|
3
|
+
* No direct imports of BlockNote, dnd-kit, Floating UI, Motion, etc. outside adapters/.
|
|
4
|
+
* Aligns with 06_ui_composition_and_adapters.md.
|
|
5
|
+
*/
|
|
6
|
+
import type { ComponentType, ReactElement } from 'react';
|
|
7
|
+
import type { FieldRendererRegistry } from '../runtime/field-renderer-registry';
|
|
8
|
+
import type { ResolvedSurfacePlan } from '../runtime/resolve-bundle';
|
|
9
|
+
import type { ActionSpec, BundleNodeKind, PanelGroupRegion, PreferenceDimensions, RegionNode, SurfaceNode, SurfacePatchOp, SurfacePatchProposal } from '../spec/types';
|
|
10
|
+
/** Context passed to renderers. */
|
|
11
|
+
export interface RenderContext {
|
|
12
|
+
plan: ResolvedSurfacePlan;
|
|
13
|
+
bindings: Record<string, unknown>;
|
|
14
|
+
preferences: PreferenceDimensions;
|
|
15
|
+
}
|
|
16
|
+
/** Field renderer registry for entity surfaces. See runtime/field-renderer-registry. */
|
|
17
|
+
export type { FieldRendererRegistry };
|
|
18
|
+
/** BlockNote adapter for rich document surfaces. Stub in Phase 2. */
|
|
19
|
+
export interface BlockNoteBundleAdapter {
|
|
20
|
+
supportsNode(kind: BundleNodeKind): boolean;
|
|
21
|
+
createSchema(registry: FieldRendererRegistry): unknown;
|
|
22
|
+
renderNode(node: SurfaceNode, ctx: RenderContext): ReactElement;
|
|
23
|
+
serialize(node: SurfaceNode): Promise<unknown>;
|
|
24
|
+
deserialize(input: unknown): Promise<SurfaceNode>;
|
|
25
|
+
}
|
|
26
|
+
/** dnd-kit adapter for drag-and-drop. Stub in Phase 2; full impl in Phase 6. */
|
|
27
|
+
export interface DragDropBundleAdapter {
|
|
28
|
+
enableSurfaceEditing(args: {
|
|
29
|
+
mutableSlots: string[];
|
|
30
|
+
onPatch: (ops: SurfacePatchOp[]) => void;
|
|
31
|
+
}): void;
|
|
32
|
+
/** When provided, wrap children for DnD when customization mode is on. */
|
|
33
|
+
DndWrapper?: ComponentType<{
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
mutableSlots: string[];
|
|
36
|
+
onPatch: (ops: SurfacePatchOp[]) => void;
|
|
37
|
+
}>;
|
|
38
|
+
}
|
|
39
|
+
/** Floating UI adapter for anchored menus and popovers. Stub in Phase 2. */
|
|
40
|
+
export interface FloatingBundleAdapter {
|
|
41
|
+
renderAnchoredMenu(args: {
|
|
42
|
+
anchorId: string;
|
|
43
|
+
items: ActionSpec[];
|
|
44
|
+
}): ReactElement;
|
|
45
|
+
}
|
|
46
|
+
/** Motion tokens derived from pace preference. */
|
|
47
|
+
export interface MotionTokens {
|
|
48
|
+
durationMs: number;
|
|
49
|
+
enableEntrance: boolean;
|
|
50
|
+
layout: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Motion adapter for layout transitions. Maps pace to tokens. */
|
|
53
|
+
export interface MotionBundleAdapter {
|
|
54
|
+
getTokens(pace: PreferenceDimensions['pace']): MotionTokens;
|
|
55
|
+
}
|
|
56
|
+
/** Renders a child region. Passed to panel adapter to avoid adapter→renderer cycle. */
|
|
57
|
+
export type RenderRegionFn = (region: RegionNode, ctx: RenderContext) => ReactElement;
|
|
58
|
+
/** Panels adapter for split-pane workbench layouts. */
|
|
59
|
+
export interface PanelLayoutAdapter {
|
|
60
|
+
renderPanelGroup(region: PanelGroupRegion, ctx: RenderContext, renderChild: RenderRegionFn): ReactElement;
|
|
61
|
+
restoreLayout(persistKey: string): Promise<number[] | null>;
|
|
62
|
+
saveLayout(persistKey: string, sizes: number[]): Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/** AI SDK adapter for planner/chat integration. Stub in Phase 2. */
|
|
65
|
+
export interface AiSdkBundleAdapter {
|
|
66
|
+
startThread(args: {
|
|
67
|
+
plannerId: string;
|
|
68
|
+
systemPrompt: string;
|
|
69
|
+
tools: Record<string, unknown>;
|
|
70
|
+
}): unknown;
|
|
71
|
+
requestPatches(args: {
|
|
72
|
+
currentPlan: ResolvedSurfacePlan;
|
|
73
|
+
userMessage: string;
|
|
74
|
+
}): Promise<SurfacePatchProposal[]>;
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Motion adapter stub. Maps pace preference to motion tokens.
|
|
3
|
+
* Aligns with 01_preference_dimensions.md animation mapping.
|
|
4
|
+
* No direct Motion imports in Phase 2 — tokens drive CSS or future Motion usage.
|
|
5
|
+
*/
|
|
6
|
+
import type { MotionBundleAdapter } from './interfaces';
|
|
7
|
+
export declare const motionAdapterStub: MotionBundleAdapter;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/adapters/motion-stub.ts
|
|
3
|
+
var PACE_TOKENS = {
|
|
4
|
+
deliberate: {
|
|
5
|
+
durationMs: 300,
|
|
6
|
+
enableEntrance: true,
|
|
7
|
+
layout: true
|
|
8
|
+
},
|
|
9
|
+
balanced: {
|
|
10
|
+
durationMs: 150,
|
|
11
|
+
enableEntrance: true,
|
|
12
|
+
layout: true
|
|
13
|
+
},
|
|
14
|
+
rapid: {
|
|
15
|
+
durationMs: 50,
|
|
16
|
+
enableEntrance: false,
|
|
17
|
+
layout: false
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var motionAdapterStub = {
|
|
21
|
+
getTokens(pace) {
|
|
22
|
+
return PACE_TOKENS[pace] ?? PACE_TOKENS.balanced;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export {
|
|
26
|
+
motionAdapterStub
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|