@pygmalionjs/pygmalion 0.5.14 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist-lib/{FrozenRoutePreview-BR5CBAD3.js → FrozenRoutePreview-CZOpvN6z.js} +1545 -1515
  2. package/dist-lib/pygmalion.js +5034 -4955
  3. package/dist-lib/testing.js +1 -1
  4. package/dist-lib/types/App.d.ts +5 -0
  5. package/dist-lib/types/canvas/CameraLayer.d.ts +9 -0
  6. package/dist-lib/types/canvas/Canvas.d.ts +19 -0
  7. package/dist-lib/types/canvas/FrameLabelBanner.d.ts +7 -0
  8. package/dist-lib/types/canvas/FrameView.d.ts +9 -0
  9. package/dist-lib/types/canvas/FrozenRoutePreview.d.ts +12 -0
  10. package/dist-lib/types/canvas/InspectOverlay.d.ts +10 -0
  11. package/dist-lib/types/canvas/LightweightCanvas.d.ts +10 -0
  12. package/dist-lib/types/canvas/ObjectControls.d.ts +22 -0
  13. package/dist-lib/types/canvas/Preview.d.ts +3 -0
  14. package/dist-lib/types/canvas/SectionBoxes.d.ts +24 -0
  15. package/dist-lib/types/canvas/ShadowRoutePreview.d.ts +14 -0
  16. package/dist-lib/types/canvas/StoryboardConnections.d.ts +31 -0
  17. package/dist-lib/types/canvas/cameraPlacement.d.ts +45 -0
  18. package/dist-lib/types/canvas/domTags.d.ts +10 -0
  19. package/dist-lib/types/canvas/layoutContext.d.ts +9 -0
  20. package/dist-lib/types/canvas/useFlowSession.d.ts +36 -0
  21. package/dist-lib/types/canvas/useFrameLod.d.ts +35 -0
  22. package/dist-lib/types/editor/adaptiveFrameLayout.d.ts +67 -0
  23. package/dist-lib/types/editor/assetEditing.d.ts +10 -0
  24. package/dist-lib/types/editor/bannerStyle.d.ts +18 -0
  25. package/dist-lib/types/editor/catalog.d.ts +101 -0
  26. package/dist-lib/types/editor/codegen.d.ts +11 -0
  27. package/dist-lib/types/editor/componentConnections.d.ts +63 -0
  28. package/dist-lib/types/editor/componentInstances.d.ts +27 -0
  29. package/dist-lib/types/editor/contentBounds.d.ts +34 -0
  30. package/dist-lib/types/editor/designCompiler.d.ts +100 -0
  31. package/dist-lib/types/editor/designImport.d.ts +448 -0
  32. package/dist-lib/types/editor/documentSync.d.ts +28 -0
  33. package/dist-lib/types/editor/domImport.d.ts +58 -0
  34. package/dist-lib/types/editor/editModePolicy.d.ts +17 -0
  35. package/dist-lib/types/editor/fiberMap.d.ts +16 -0
  36. package/dist-lib/types/editor/flowSessionScheduler.d.ts +97 -0
  37. package/dist-lib/types/editor/flowSessions.d.ts +137 -0
  38. package/dist-lib/types/editor/frameHeight.d.ts +14 -0
  39. package/dist-lib/types/editor/frameIdentity.d.ts +9 -0
  40. package/dist-lib/types/editor/frameLabels.d.ts +124 -0
  41. package/dist-lib/types/editor/frameLanes.d.ts +62 -0
  42. package/dist-lib/types/editor/frameLod.d.ts +123 -0
  43. package/dist-lib/types/editor/frameLodRuntime.d.ts +64 -0
  44. package/dist-lib/types/editor/framePointerHit.d.ts +67 -0
  45. package/dist-lib/types/editor/framePointerIntent.d.ts +53 -0
  46. package/dist-lib/types/editor/framePreviewChannels.d.ts +26 -0
  47. package/dist-lib/types/editor/framePreviewKeys.d.ts +21 -0
  48. package/dist-lib/types/editor/frameWheelRouting.d.ts +16 -0
  49. package/dist-lib/types/editor/frozenImport.d.ts +70 -0
  50. package/dist-lib/types/editor/host.d.ts +133 -0
  51. package/dist-lib/types/editor/inspect.d.ts +225 -0
  52. package/dist-lib/types/editor/interactiveStates.d.ts +103 -0
  53. package/dist-lib/types/editor/livePreviewInstances.d.ts +93 -0
  54. package/dist-lib/types/editor/liveScreens.d.ts +20 -0
  55. package/dist-lib/types/editor/nodeSelector.d.ts +20 -0
  56. package/dist-lib/types/editor/previewBootstrap.d.ts +189 -0
  57. package/dist-lib/types/editor/previewEnvironmentControls.d.ts +33 -0
  58. package/dist-lib/types/editor/previewHydration.d.ts +5 -0
  59. package/dist-lib/types/editor/previewIntent.d.ts +17 -0
  60. package/dist-lib/types/editor/previewSweep.d.ts +129 -0
  61. package/dist-lib/types/editor/previewWarmup.d.ts +77 -0
  62. package/dist-lib/types/editor/projectBootGate.d.ts +45 -0
  63. package/dist-lib/types/editor/projectRuntime.d.ts +138 -0
  64. package/dist-lib/types/editor/registry.d.ts +38 -0
  65. package/dist-lib/types/editor/routePreview.d.ts +285 -0
  66. package/dist-lib/types/editor/routePreviewArtifactV2.d.ts +34 -0
  67. package/dist-lib/types/editor/routePreviewArtifactV3.d.ts +64 -0
  68. package/dist-lib/types/editor/routePreviewPlacement.d.ts +9 -0
  69. package/dist-lib/types/editor/routePreviewStatus.d.ts +48 -0
  70. package/dist-lib/types/editor/scenarioCoverage.d.ts +167 -0
  71. package/dist-lib/types/editor/screenCards.d.ts +101 -0
  72. package/dist-lib/types/editor/screenDimensions.d.ts +63 -0
  73. package/dist-lib/types/editor/screenFlows.d.ts +47 -0
  74. package/dist-lib/types/editor/screenInteractions.d.ts +4 -0
  75. package/dist-lib/types/editor/screenLists.d.ts +40 -0
  76. package/dist-lib/types/editor/screenStateGroups.d.ts +36 -0
  77. package/dist-lib/types/editor/screenshotBitmaps.d.ts +20 -0
  78. package/dist-lib/types/editor/sectionHeaders.d.ts +117 -0
  79. package/dist-lib/types/editor/shadowPreview.d.ts +103 -0
  80. package/dist-lib/types/editor/sharedSource.d.ts +40 -0
  81. package/dist-lib/types/editor/sourceJournal.d.ts +27 -0
  82. package/dist-lib/types/editor/store.d.ts +838 -0
  83. package/dist-lib/types/editor/storyboardComposition.d.ts +116 -0
  84. package/dist-lib/types/editor/storyboardDiscovery.d.ts +105 -0
  85. package/dist-lib/types/editor/storyboardEnvironment.d.ts +22 -0
  86. package/dist-lib/types/editor/storyboardGraph.d.ts +120 -0
  87. package/dist-lib/types/editor/storyboardGraphRuntime.d.ts +12 -0
  88. package/dist-lib/types/editor/storyboardGraphView.d.ts +104 -0
  89. package/dist-lib/types/editor/surfaceDemands.d.ts +138 -0
  90. package/dist-lib/types/editor/tokens.d.ts +26 -0
  91. package/dist-lib/types/editor/useObserved.d.ts +1 -0
  92. package/dist-lib/types/editor/viewportPresets.d.ts +21 -0
  93. package/dist-lib/types/editor/visualQa.d.ts +50 -0
  94. package/dist-lib/types/lib.d.ts +365 -0
  95. package/dist-lib/types/shell/ApplyPanel.d.ts +1 -0
  96. package/dist-lib/types/shell/AssetsPanel.d.ts +3 -0
  97. package/dist-lib/types/shell/CanvasStatusProbe.d.ts +5 -0
  98. package/dist-lib/types/shell/CodePanel.d.ts +1 -0
  99. package/dist-lib/types/shell/ComponentStateControls.d.ts +108 -0
  100. package/dist-lib/types/shell/ContextMenu.d.ts +1 -0
  101. package/dist-lib/types/shell/LayerTree.d.ts +7 -0
  102. package/dist-lib/types/shell/PropsPanel.d.ts +1 -0
  103. package/dist-lib/types/shell/StoryboardGraphPanel.d.ts +23 -0
  104. package/dist-lib/types/shell/ZoomIndicator.d.ts +4 -0
  105. package/dist-lib/types/shell/qaPresentation.d.ts +9 -0
  106. package/dist-lib/types/testing.d.ts +8 -0
  107. package/dist-lib/types/ui/SourceRefControl.d.ts +38 -0
  108. package/dist-lib/types/ui/icons.d.ts +30 -0
  109. package/node/dev-mirror.mjs +63 -17
  110. package/package.json +7 -8
  111. package/testing.d.ts +0 -26
  112. package/types.d.ts +0 -2675
@@ -0,0 +1,38 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { PygmalionSourceRef } from '../editor/projectRuntime';
3
+ export interface SourceRefControlProps {
4
+ /** Revision the mirror follows now. */
5
+ value: string | null;
6
+ /** Commit currently rendered by the mirror. Used to expose a newer ref tip. */
7
+ currentCommit?: string | null;
8
+ /** Revisions the mirror can be repointed at. */
9
+ refs: readonly PygmalionSourceRef[];
10
+ onChange(ref: string): void;
11
+ /** Blocks selection while the mirror rebuilds. */
12
+ busy?: boolean;
13
+ /** Revision used when none is pinned, offered as the first entry. */
14
+ defaultRef?: string | null;
15
+ /** Ref that renders uncommitted work. Omitted from the list when absent. */
16
+ worktreeRef?: string | null;
17
+ /** Accessible name for the control. */
18
+ label?: string;
19
+ /** Menu label for reloading the selected revision after its tip moves. */
20
+ refreshLabel?: string;
21
+ /**
22
+ * Trigger content. A host renders its own status chip here, so the control
23
+ * blends into an existing toolbar instead of adding a second field.
24
+ * Defaults to the selected revision.
25
+ */
26
+ children?: ReactNode;
27
+ className?: string;
28
+ }
29
+ /**
30
+ * Selects the revision the dev mirror follows. Switching rebuilds the mirror,
31
+ * screen inventory, and preview on that revision, so the editor reloads.
32
+ *
33
+ * The menu is a native select layered over the trigger: the host owns how the
34
+ * trigger looks, while keyboard access, mobile behavior, and menu placement stay
35
+ * with the platform. Pygmalion owns this control because the mirror lifecycle
36
+ * belongs to the tool, not to a consuming application.
37
+ */
38
+ export declare function SourceRefControl({ value, currentCommit, refs, onChange, busy, defaultRef, worktreeRef, label, refreshLabel, children, className, }: SourceRefControlProps): import("react").JSX.Element;
@@ -0,0 +1,30 @@
1
+ type IconProps = {
2
+ size?: number;
3
+ };
4
+ export declare const CursorIcon: ({ size }: IconProps) => import("react").JSX.Element;
5
+ export declare const HandIcon: ({ size }: IconProps) => import("react").JSX.Element;
6
+ export declare const ZoomIcon: ({ size }: IconProps) => import("react").JSX.Element;
7
+ export declare const FrameIcon: ({ size }: IconProps) => import("react").JSX.Element;
8
+ export declare const SectionIcon: ({ size }: IconProps) => import("react").JSX.Element;
9
+ export declare const RowsIcon: ({ size }: IconProps) => import("react").JSX.Element;
10
+ export declare const ColumnsIcon: ({ size }: IconProps) => import("react").JSX.Element;
11
+ export declare const TextIcon: ({ size }: IconProps) => import("react").JSX.Element;
12
+ export declare const CodeIcon: ({ size }: IconProps) => import("react").JSX.Element;
13
+ export declare const MinusIcon: ({ size }: IconProps) => import("react").JSX.Element;
14
+ export declare const PlusIcon: ({ size }: IconProps) => import("react").JSX.Element;
15
+ export declare const UndoIcon: ({ size }: IconProps) => import("react").JSX.Element;
16
+ export declare const RedoIcon: ({ size }: IconProps) => import("react").JSX.Element;
17
+ export declare const TrashIcon: ({ size }: IconProps) => import("react").JSX.Element;
18
+ export declare const GaugeIcon: ({ size }: IconProps) => import("react").JSX.Element;
19
+ export declare const AlignLeftIcon: ({ size }: IconProps) => import("react").JSX.Element;
20
+ export declare const AlignCenterIcon: ({ size }: IconProps) => import("react").JSX.Element;
21
+ export declare const AlignRightIcon: ({ size }: IconProps) => import("react").JSX.Element;
22
+ export declare const PlayIcon: ({ size }: IconProps) => import("react").JSX.Element;
23
+ export declare const ComponentIcon: ({ size }: IconProps) => import("react").JSX.Element;
24
+ export declare const EyeIcon: ({ size }: IconProps) => import("react").JSX.Element;
25
+ export declare const EyeOffIcon: ({ size }: IconProps) => import("react").JSX.Element;
26
+ export declare const LockIcon: ({ size }: IconProps) => import("react").JSX.Element;
27
+ export declare const ChevronIcon: ({ size, open }: IconProps & {
28
+ open: boolean;
29
+ }) => import("react").JSX.Element;
30
+ export {};
@@ -29,6 +29,9 @@ const LEASE_HEARTBEAT_MS = 2_000;
29
29
  // costs a checkout and a dependency install, so the reaper errs toward keeping.
30
30
  const DEFAULT_MIRROR_GRACE_MS = 6 * 60 * 60 * 1_000;
31
31
  const LEASE_WAIT_TIMEOUT_MS = 180_000;
32
+ // A serving editor holds this for its whole session. It marks the checkout as in
33
+ // use for the reaper; it must never stand in the way of a sync.
34
+ const SERVE_LEASE_LABEL = 'serve';
32
35
 
33
36
  export function resolveDevMirrorInventoryOutputRoot(inventory, mirrorAppRoot) {
34
37
  return path.resolve(inventory?.outputRoot ?? mirrorAppRoot);
@@ -469,10 +472,25 @@ export async function acquireDevMirrorLease({ repoRoot, mirrorRoot, label = 'cap
469
472
  };
470
473
  }
471
474
 
472
- async function waitForLeasesToClear(repoRoot, mirrorRoot, timeoutMs = LEASE_WAIT_TIMEOUT_MS) {
473
- const deadline = Date.now() + timeoutMs;
475
+ /**
476
+ * Leases that a sync must wait out.
477
+ *
478
+ * A reader lease is short: a capture holds one while it walks the checkout, and
479
+ * moving the files under it would corrupt what it is reading. A serve lease is
480
+ * the opposite — it lasts as long as an editor is open, and it exists so a
481
+ * reaper can tell a live checkout from an abandoned one. Treating the two the
482
+ * same wedged the second editor on a ref: it waited three minutes for a lease
483
+ * that, by design, never clears, and then reported the mirror as held.
484
+ */
485
+ export function leasesBlockingSync(leases) {
486
+ return leases.filter((lease) => lease.label !== SERVE_LEASE_LABEL);
487
+ }
488
+
489
+ async function waitForLeasesToClear(repoRoot, mirrorRoot, timeoutMs) {
490
+ const limit = Number.isFinite(timeoutMs) ? timeoutMs : LEASE_WAIT_TIMEOUT_MS;
491
+ const deadline = Date.now() + limit;
474
492
  while (true) {
475
- const held = await freshLeases(repoRoot, mirrorRoot);
493
+ const held = leasesBlockingSync(await freshLeases(repoRoot, mirrorRoot));
476
494
  if (held.length === 0) return;
477
495
  if (Date.now() >= deadline) {
478
496
  const labels = [...new Set(held.map((lease) => lease.label))].join(', ');
@@ -844,7 +862,7 @@ export function pygmalionDevMirrorPlugin(options) {
844
862
  const lease = await acquireDevMirrorLease({
845
863
  repoRoot,
846
864
  mirrorRoot: activeRoot,
847
- label: 'serve',
865
+ label: SERVE_LEASE_LABEL,
848
866
  }).catch(() => null);
849
867
  if (lease) serveLease = { mirrorRoot: activeRoot, lease };
850
868
  };
@@ -854,6 +872,9 @@ export function pygmalionDevMirrorPlugin(options) {
854
872
  await held?.lease.release().catch(() => undefined);
855
873
  };
856
874
 
875
+ // Whether anything has asked for the checkout yet. The runtime is built once,
876
+ // on first demand, and every later demand joins the same work.
877
+ let runtimeRequested = false;
857
878
  let previewChild = null;
858
879
  let previewPort = null;
859
880
  let syncPromise = null;
@@ -1117,8 +1138,9 @@ export function pygmalionDevMirrorPlugin(options) {
1117
1138
  let warning = null;
1118
1139
  try {
1119
1140
  return await withSharedSyncLock(async () => {
1120
- // A capture reading this checkout must finish before it moves.
1121
- await waitForLeasesToClear(repoRoot, mirrorRoot);
1141
+ // A capture reading this checkout must finish before it moves. A serving
1142
+ // editor is not that kind of holder — see leasesBlockingSync.
1143
+ await waitForLeasesToClear(repoRoot, mirrorRoot, options.leaseWaitTimeoutMs);
1122
1144
  const mirrorState = await syncDevMirrorWorktree({
1123
1145
  repoRoot,
1124
1146
  mirrorRoot,
@@ -1195,14 +1217,27 @@ export function pygmalionDevMirrorPlugin(options) {
1195
1217
  process.once('exit', () => {
1196
1218
  previewChild?.kill('SIGTERM');
1197
1219
  });
1198
- // Synchronizes once when the editor server runs. Screen mount and manual refresh requests reuse the same Promise.
1199
- void syncMirror().then(() =>
1200
- reapDevMirrorWorktrees({
1201
- repoRoot,
1202
- mirrorBaseRoot,
1203
- keep: [mirrorRoot],
1204
- })
1205
- .then(({ removed }) => {
1220
+ // Nothing is prepared until something asks for it.
1221
+ //
1222
+ // Opening the editor used to build the checkout: fetch, worktree, install,
1223
+ // inventory — minutes before the first pixel, on every start, even when all
1224
+ // anyone did was look at the catalog. The captured frames are already on
1225
+ // disk and belong to the consuming app, so browsing needs none of it.
1226
+ //
1227
+ // What genuinely needs the checkout asks for it: a refresh or a revision
1228
+ // switch (below), and any request for the preview origin — the live frame,
1229
+ // its assets, a capture. Those requests kick the work off and answer
1230
+ // "preparing" until it lands, which is what they already did while a
1231
+ // start-time sync was in flight.
1232
+ const startRuntime = () => {
1233
+ if (runtimeRequested) return;
1234
+ runtimeRequested = true;
1235
+ void syncMirror()
1236
+ .then(() =>
1237
+ reapDevMirrorWorktrees({ repoRoot, mirrorBaseRoot, keep: [mirrorRoot] }),
1238
+ )
1239
+ .then((result) => {
1240
+ const removed = result?.removed ?? [];
1206
1241
  if (removed.length > 0) {
1207
1242
  console.log(
1208
1243
  `[pygmalion] released ${removed.length} unused dev screen checkout(s): ${removed
@@ -1211,8 +1246,8 @@ export function pygmalionDevMirrorPlugin(options) {
1211
1246
  );
1212
1247
  }
1213
1248
  })
1214
- .catch(() => undefined),
1215
- );
1249
+ .catch(() => undefined);
1250
+ };
1216
1251
 
1217
1252
  server.middlewares.use(async (req, res, next) => {
1218
1253
  const url = new URL(req.url ?? '/', 'http://localhost');
@@ -1249,6 +1284,10 @@ export function pygmalionDevMirrorPlugin(options) {
1249
1284
  }
1250
1285
  let nextStatus;
1251
1286
  try {
1287
+ // An explicit refresh is a demand like any other — mark the runtime as
1288
+ // started so a later preview request joins this work instead of
1289
+ // scheduling a second one.
1290
+ runtimeRequested = true;
1252
1291
  nextStatus = await syncMirror(requestedRef);
1253
1292
  } catch (error) {
1254
1293
  json(res, 400, {
@@ -1265,7 +1304,14 @@ export function pygmalionDevMirrorPlugin(options) {
1265
1304
  // no longer matches the revision this origin is named after.
1266
1305
  const current = await verifiedStatus();
1267
1306
  if (current.state !== 'ready' || previewPort == null) {
1268
- json(res, 503, { ok: false, error: current.error ?? 'Preparing the dev screen' });
1307
+ // This is the first thing that actually needs the checkout, so it is
1308
+ // the thing that starts it.
1309
+ startRuntime();
1310
+ json(res, 503, {
1311
+ ok: false,
1312
+ error: current.error ?? 'Preparing the dev screen',
1313
+ preparing: true,
1314
+ });
1269
1315
  return;
1270
1316
  }
1271
1317
  proxyRequest(req, res, previewPort, prefix);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.5.14",
3
+ "version": "0.5.16",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -9,10 +9,10 @@
9
9
  },
10
10
  "type": "module",
11
11
  "module": "./dist-lib/pygmalion.js",
12
- "types": "./types.d.ts",
12
+ "types": "./dist-lib/types/lib.d.ts",
13
13
  "exports": {
14
14
  ".": {
15
- "types": "./types.d.ts",
15
+ "types": "./dist-lib/types/lib.d.ts",
16
16
  "default": "./dist-lib/pygmalion.js"
17
17
  },
18
18
  "./vite": {
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "./dev-view": "./node/dev-view.vite.mjs",
27
27
  "./testing": {
28
- "types": "./testing.d.ts",
28
+ "types": "./dist-lib/types/testing.d.ts",
29
29
  "default": "./dist-lib/testing.js"
30
30
  },
31
31
  "./style.css": "./dist-lib/style.css"
@@ -58,19 +58,18 @@
58
58
  "node/vite.mjs",
59
59
  "qa.d.ts",
60
60
  "storyboard.d.ts",
61
- "types.d.ts",
62
- "testing.d.ts",
63
61
  "vite.d.ts"
64
62
  ],
65
63
  "scripts": {
66
64
  "dev": "vite",
67
65
  "build": "tsc && vite build",
68
- "build:lib": "vite build --config vite.lib.config.ts",
66
+ "build:lib": "vite build --config vite.lib.config.ts && npm run build:types",
69
67
  "test": "node --test node/*.test.mjs src/*.test.mjs src/**/*.test.mjs",
70
68
  "lint:language": "node scripts/lint-language.mjs",
71
69
  "typecheck": "tsc --noEmit",
72
70
  "prepublishOnly": "npm run lint:language && npm test && npm run typecheck && npm run build:lib",
73
- "preview": "vite preview"
71
+ "preview": "vite preview",
72
+ "build:types": "node scripts/emit-types.mjs"
74
73
  },
75
74
  "peerDependencies": {
76
75
  "react": "^19.0.0",
package/testing.d.ts DELETED
@@ -1,26 +0,0 @@
1
- import type { ReactElement } from 'react';
2
- import type {
3
- ComponentRegistry,
4
- NodeJSON,
5
- } from './types';
6
-
7
- export declare function FrozenRoutePreviewView(props: {
8
- page: unknown;
9
- snapshot: string;
10
- height: number;
11
- onDocumentReady?: (iframe: HTMLIFrameElement) => void;
12
- onReplayIssuesChange?: (issues: string[]) => void;
13
- }): ReactElement;
14
-
15
- export declare class NodeModel {
16
- constructor(
17
- type: 'rows' | 'columns' | 'text' | 'component',
18
- patch?: Partial<NodeJSON>,
19
- );
20
- }
21
-
22
- export declare const editor: any;
23
- export declare function serializeNode(node: NodeModel): NodeJSON;
24
- export declare function setComponentRegistry(
25
- registry: ComponentRegistry,
26
- ): void;