proto-plugin 0.1.1 → 0.1.3

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 (109) hide show
  1. package/AGENTS.md +308 -29
  2. package/README.md +48 -6
  3. package/package.json +2 -1
  4. package/scripts/lib/host-config.example.sh +1 -1
  5. package/scripts/verify-prototype-preview-states.mjs +57 -0
  6. package/src/components/platform-ui/controls-panel-select.tsx +4 -3
  7. package/src/components/platform-ui/prototype-tool-dialog.tsx +117 -0
  8. package/src/components/platform-ui/sidebar.tsx +34 -16
  9. package/src/components/prototype-gallery-client.tsx +77 -37
  10. package/src/components/prototypes/design-exploration-variant-preview-shell.tsx +43 -0
  11. package/src/components/prototypes/prototype-brief-field.tsx +33 -0
  12. package/src/components/prototypes/prototype-change-log-panel.tsx +59 -55
  13. package/src/components/prototypes/prototype-comment-provider.tsx +0 -1
  14. package/src/components/prototypes/prototype-comment-storage-empty-state.tsx +41 -0
  15. package/src/components/prototypes/prototype-create-exploration-modal.tsx +41 -64
  16. package/src/components/prototypes/prototype-create-prototype-modal.tsx +102 -0
  17. package/src/components/prototypes/prototype-create-state-modal.tsx +31 -70
  18. package/src/components/prototypes/prototype-default-state-map-page.tsx +4 -4
  19. package/src/components/prototypes/prototype-design-brief-modal.tsx +10 -10
  20. package/src/components/prototypes/prototype-design-brief-panel.tsx +261 -231
  21. package/src/components/prototypes/prototype-floating-pill.module.scss +16 -9
  22. package/src/components/prototypes/prototype-mobbin-gallery.tsx +18 -2
  23. package/src/components/prototypes/prototype-review-chrome.tsx +21 -2
  24. package/src/components/prototypes/prototype-review-sidebar.tsx +7 -37
  25. package/src/components/prototypes/prototype-spec-panel-content.tsx +5 -5
  26. package/src/components/prototypes/prototype-spec-panel-empty-state.tsx +53 -0
  27. package/src/components/prototypes/prototype-state-canvas-overlay.module.scss +20 -47
  28. package/src/components/prototypes/prototype-state-canvas-overlay.tsx +4 -42
  29. package/src/components/prototypes/prototype-variant-explorer.tsx +32 -78
  30. package/src/components/prototypes/prototype-vercel-preview-modal.tsx +4 -3
  31. package/src/components/prototypes/state-map-annotation-tooltip.module.scss +7 -7
  32. package/src/components/shell/prototype-component-library-empty-state.tsx +59 -0
  33. package/src/components/shell/prototype-gallery-nav.tsx +124 -0
  34. package/src/components/shell/prototype-gallery-shell.tsx +45 -13
  35. package/src/components/shell/prototype-link-source-modal.tsx +100 -0
  36. package/src/components/shell/prototype-plugin-update-popup.tsx +74 -0
  37. package/src/components/ui/button.tsx +4 -2
  38. package/src/components/ui/dialog.tsx +44 -5
  39. package/src/components/ui/dropdown-menu.tsx +2 -2
  40. package/src/components/ui/input.tsx +1 -1
  41. package/src/components/ui/label.tsx +1 -1
  42. package/src/components/ui/option-select.tsx +3 -3
  43. package/src/components/ui/scroll-area.tsx +1 -1
  44. package/src/components/ui/separator.tsx +1 -1
  45. package/src/components/ui/slider.tsx +1 -1
  46. package/src/components/ui/switch.tsx +1 -1
  47. package/src/components/ui/tabs.tsx +1 -1
  48. package/src/components/ui/toggle-group.tsx +1 -1
  49. package/src/components/ui/tooltip.tsx +2 -2
  50. package/src/config/create-prototype-component-library-page.tsx +74 -0
  51. package/src/config/create-prototype-gallery-page.tsx +26 -23
  52. package/src/config/create-prototype-host-app.tsx +3 -15
  53. package/src/config/create-prototype-site-layout.tsx +14 -1
  54. package/src/config/create-prototype-state-map-page.tsx +44 -0
  55. package/src/config/prototype-site-layout-client.tsx +23 -6
  56. package/src/config/with-prototype.ts +54 -0
  57. package/src/index.ts +71 -11
  58. package/src/lib/copy-text-with-attachments.ts +35 -0
  59. package/src/lib/format-source-directory-name.ts +12 -0
  60. package/src/lib/platform-ui/sidebar-interactive-cursor.ts +19 -0
  61. package/src/lib/pr-split/build-pr-split-plan-prompt.ts +81 -0
  62. package/src/lib/pr-split/pr-split-highlight.ts +3 -0
  63. package/src/lib/prototype-comments/core/annotation-target.ts +12 -3
  64. package/src/lib/prototype-comments/core/comment-capture-blocked.ts +24 -0
  65. package/src/lib/prototype-comments/core/screenshot.ts +68 -31
  66. package/src/lib/prototype-comments/core/storage.ts +3 -6
  67. package/src/lib/prototype-comments/core/types.ts +2 -0
  68. package/src/lib/prototype-comments/hooks/useLayoutShift.ts +9 -2
  69. package/src/lib/prototype-comments/react/CommentCaptureToolbar.tsx +45 -10
  70. package/src/lib/prototype-comments/react/CommentProvider.tsx +19 -0
  71. package/src/lib/prototype-comments/ui/CommentModeToggleButton.tsx +34 -0
  72. package/src/lib/prototype-comments/ui/CommentsGrid.tsx +8 -16
  73. package/src/lib/prototype-comments/ui/CommentsSidebar.tsx +5 -19
  74. package/src/lib/prototype-comments/ui/capture-styles.module.scss +5 -1
  75. package/src/lib/prototype-comments/ui/comments-grid-card.module.scss +0 -7
  76. package/src/lib/prototypes/build-design-exploration-renderers.ts +44 -0
  77. package/src/lib/prototypes/changelog-description-prompt.ts +58 -0
  78. package/src/lib/prototypes/comment-storage-setup-prompt.ts +124 -0
  79. package/src/lib/prototypes/create-prototype-prompt.ts +96 -0
  80. package/src/lib/prototypes/create-prototype-registry.ts +5 -0
  81. package/src/lib/prototypes/design-exploration-types.ts +261 -10
  82. package/src/lib/prototypes/link-source-prompt.ts +93 -0
  83. package/src/lib/prototypes/populate-component-library-prompt.ts +64 -0
  84. package/src/lib/prototypes/proto-plugin-version.ts +19 -0
  85. package/src/lib/prototypes/prototype-config-types.ts +2 -0
  86. package/src/lib/prototypes/prototype-layout-classes.ts +19 -0
  87. package/src/lib/prototypes/prototype-preview-state-registry.ts +5 -2
  88. package/src/lib/prototypes/prototype-review-context.tsx +4 -4
  89. package/src/lib/prototypes/prototype-state-canvas-constants.ts +1 -1
  90. package/src/lib/prototypes/prototype-state-canvas-types.ts +0 -2
  91. package/src/lib/prototypes/prototype-storage-status.ts +31 -0
  92. package/src/lib/prototypes/sort-prototypes-by-creation-date.ts +43 -0
  93. package/src/lib/prototypes/upgrade-proto-plugin-prompt.ts +40 -0
  94. package/src/lib/prototypes/use-proto-plugin-version-check.ts +67 -0
  95. package/src/lib/prototypes/use-prototype-tool-theme.tsx +1 -1
  96. package/src/lib/prototypes/use-toolbar-hover-panel.ts +12 -1
  97. package/src/lib/prototypes/variant-set-lucide-icon.tsx +24 -0
  98. package/src/lib/tool-portal.ts +24 -0
  99. package/src/lib/use-copy-to-clipboard.ts +7 -13
  100. package/src/server/create-host-api-route.ts +14 -16
  101. package/src/server/create-prototype-api-routes.ts +0 -115
  102. package/src/server/proto-plugin-version-route.ts +145 -0
  103. package/src/server/prototype-storage-status-route.ts +29 -0
  104. package/src/server.ts +2 -8
  105. package/src/styles/globals.css +357 -69
  106. package/src/lib/prototypes/design-exploration-default-storage.ts +0 -68
  107. package/src/lib/prototypes/design-exploration-default.ts +0 -51
  108. package/src/lib/prototypes/use-design-exploration-default.ts +0 -114
  109. package/src/server/redis/prototype-design-exploration-default.ts +0 -32
@@ -2,10 +2,15 @@
2
2
 
3
3
  import { PrototypeComponent } from "@prototype/components/prototypes/prototype-target";
4
4
  import { Button } from "@prototype/components/ui/button";
5
+ import { getPrototypePortalContainer } from "@prototype/lib/tool-portal";
5
6
  import { cn } from "@prototype/lib/utils";
6
7
  import type { MobbinReference } from "@prototype/lib/prototypes/design-exploration-types";
7
8
  import { ChevronLeft, ChevronRight, ExternalLink, X, ZoomIn } from "lucide-react";
8
9
  import { useCallback, useEffect, useState } from "react";
10
+ import { createPortal } from "react-dom";
11
+
12
+ /** Above prototype preview + design brief dialog (z-50), below review chrome (1050). */
13
+ const MOBBIN_LIGHTBOX_Z_INDEX = 1040;
9
14
 
10
15
  const GALLERY_SECTION_TITLE_CLASS = "tool-chip tool-chip-pink";
11
16
 
@@ -137,6 +142,12 @@ function MobbinReferenceLightbox({
137
142
  return () => window.removeEventListener("keydown", handleKeyDown);
138
143
  }, [goNext, goPrev, onClose]);
139
144
 
145
+ const [portalTarget, setPortalTarget] = useState<HTMLElement | null>(null);
146
+
147
+ useEffect(() => {
148
+ setPortalTarget(getPrototypePortalContainer() ?? null);
149
+ }, []);
150
+
140
151
  useEffect(() => {
141
152
  const previousOverflow = document.body.style.overflow;
142
153
  document.body.style.overflow = "hidden";
@@ -145,10 +156,11 @@ function MobbinReferenceLightbox({
145
156
  };
146
157
  }, []);
147
158
 
148
- return (
159
+ const lightbox = (
149
160
  <PrototypeComponent
150
161
  id={`${galleryId}.lightbox`}
151
- className="fixed inset-0 z-(--z-modal) flex items-center justify-center p-4 sm:p-8"
162
+ className="fixed inset-0 flex items-center justify-center p-4 sm:p-8"
163
+ style={{ zIndex: MOBBIN_LIGHTBOX_Z_INDEX }}
152
164
  role="dialog"
153
165
  aria-modal="true"
154
166
  aria-label={`${reference.appName} Mobbin reference`}
@@ -254,6 +266,10 @@ function MobbinReferenceLightbox({
254
266
  </div>
255
267
  </PrototypeComponent>
256
268
  );
269
+
270
+ if (!portalTarget) return null;
271
+
272
+ return createPortal(lightbox, portalTarget);
257
273
  }
258
274
 
259
275
  export function PrototypeMobbinGallery({
@@ -48,6 +48,7 @@ import {
48
48
  LayoutGrid,
49
49
  MapPin,
50
50
  MessageSquare,
51
+ MessageSquarePlus,
51
52
  Monitor,
52
53
  Moon,
53
54
  Network,
@@ -250,6 +251,7 @@ function ReviewToolbarButton({
250
251
  onClick,
251
252
  hoverOnly = false,
252
253
  hoverTriggerProps,
254
+ commentModeToggle = false,
253
255
  children,
254
256
  }: {
255
257
  label: string;
@@ -260,6 +262,7 @@ function ReviewToolbarButton({
260
262
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
261
263
  hoverOnly?: boolean;
262
264
  hoverTriggerProps?: ToolbarTooltipAnchorProps;
265
+ commentModeToggle?: boolean;
263
266
  children: ReactNode;
264
267
  }) {
265
268
  return (
@@ -272,6 +275,9 @@ function ReviewToolbarButton({
272
275
  pressed && !highlightActive && styles.toolbarButtonHovered,
273
276
  )}
274
277
  data-active={highlightActive && pressed ? "true" : undefined}
278
+ data-prototype-comment-mode-toggle={
279
+ commentModeToggle ? true : undefined
280
+ }
275
281
  aria-label={label}
276
282
  aria-pressed={pressed}
277
283
  onClick={(event) => {
@@ -1121,6 +1127,21 @@ export function PrototypeReviewChrome({
1121
1127
  </DropdownMenuContent>
1122
1128
  </DropdownMenu>
1123
1129
 
1130
+ {onToggleCommentMode ? (
1131
+ <ReviewToolbarButton
1132
+ label={
1133
+ isCommentModeActive ? "Exit comment mode" : "Add comment"
1134
+ }
1135
+ tooltip="Add comment"
1136
+ pressed={isCommentModeActive ?? false}
1137
+ highlightActive={isCommentModeActive ?? false}
1138
+ commentModeToggle
1139
+ onClick={() => onToggleCommentMode()}
1140
+ >
1141
+ <MessageSquarePlus size={16} strokeWidth={2} />
1142
+ </ReviewToolbarButton>
1143
+ ) : null}
1144
+
1124
1145
  <ReviewToolbarButton
1125
1146
  label={
1126
1147
  review.open && review.sidebarPanel === "comments"
@@ -1204,8 +1225,6 @@ export function PrototypeReviewChrome({
1204
1225
  onSelect={onSelect}
1205
1226
  selectedId={selectedId}
1206
1227
  onClose={onClose}
1207
- isCommentModeActive={isCommentModeActive}
1208
- onToggleCommentMode={onToggleCommentMode}
1209
1228
  />,
1210
1229
  sidebarPortalTarget,
1211
1230
  )
@@ -7,35 +7,27 @@ import { useCommentStore } from "@prototype/lib/prototype-comments/react/Comment
7
7
  import { CommentHeaderCount } from "@prototype/lib/prototype-comments/ui/CommentHeaderCount";
8
8
  import { CommentsGrid } from "@prototype/lib/prototype-comments/ui/CommentsGrid";
9
9
  import { usePrototypeReview } from "@prototype/lib/prototypes/prototype-review-context";
10
- import {
11
- SIDEBAR_ICON_BUTTON_CLASS,
12
- Sidebar,
13
- } from "@prototype/components/platform-ui/sidebar";
14
- import { IconButton } from "@prototype/components/platform-ui/icon-button";
15
- import { cn } from "@prototype/lib/utils";
10
+ import { Sidebar } from "@prototype/components/platform-ui/sidebar";
16
11
  import { EmptyState } from "@prototype/components/platform-ui/empty-state";
17
12
  import { PrototypeChangeLogPanel } from "@prototype/components/prototypes/prototype-change-log-panel";
13
+ import { PrototypeCommentStorageEmptyState } from "@prototype/components/prototypes/prototype-comment-storage-empty-state";
14
+ import { PrototypeSpecPanelEmptyState } from "@prototype/components/prototypes/prototype-spec-panel-empty-state";
18
15
  import { miniPillTextFromLabel } from "@prototype/components/prototypes/mini-pill-label";
19
- import { MessageSquarePlus } from "lucide-react";
20
16
  import { useMemo } from "react";
21
17
 
22
18
  type PrototypeReviewSidebarProps = {
23
19
  onSelect: (id: string) => void;
24
20
  selectedId?: string | null;
25
21
  onClose: () => void;
26
- isCommentModeActive?: boolean;
27
- onToggleCommentMode?: () => void;
28
22
  };
29
23
 
30
24
  export function PrototypeReviewSidebar({
31
25
  onSelect,
32
26
  selectedId,
33
27
  onClose,
34
- isCommentModeActive = false,
35
- onToggleCommentMode,
36
28
  }: PrototypeReviewSidebarProps) {
37
29
  const review = usePrototypeReview();
38
- const { annotations, deleteAnnotation, updateAnnotation, resolveAnnotation, storageError, storageReady } = useCommentStore();
30
+ const { annotations, deleteAnnotation, updateAnnotation, resolveAnnotation, storageError, storageReady, storageConfigured } = useCommentStore();
39
31
  const isVariantsPanel = review.sidebarPanel === "variants";
40
32
  const isChangeLogPanel = review.sidebarPanel === "change-log";
41
33
  const isSpecPanel = review.sidebarPanel === "spec";
@@ -94,25 +86,6 @@ export function PrototypeReviewSidebar({
94
86
  />
95
87
  ) : undefined
96
88
  }
97
- headerActions={
98
- isCommentsPanel && onToggleCommentMode ? (
99
- <IconButton
100
- type="button"
101
- onClick={onToggleCommentMode}
102
- onMouseDown={(event) => event.preventDefault()}
103
- aria-label={
104
- isCommentModeActive ? "Exit comment mode" : "Add comment"
105
- }
106
- aria-pressed={isCommentModeActive}
107
- className={cn(
108
- SIDEBAR_ICON_BUTTON_CLASS,
109
- isCommentModeActive && "bg-accent text-accent-foreground",
110
- )}
111
- >
112
- <MessageSquarePlus size={16} strokeWidth={2} />
113
- </IconButton>
114
- ) : undefined
115
- }
116
89
  onClose={
117
90
  isVariantsPanel
118
91
  ? review.closeVariants
@@ -135,12 +108,7 @@ export function PrototypeReviewSidebar({
135
108
  widthCssVar="--comments-sidebar-width"
136
109
  >
137
110
  {isSpecPanel ? (
138
- specContent ?? (
139
- <EmptyState>
140
- No PRs configured yet. Add a PR split spec on this prototype to
141
- see implementation cards here.
142
- </EmptyState>
143
- )
111
+ specContent ?? <PrototypeSpecPanelEmptyState />
144
112
  ) : isVariantsPanel ? (
145
113
  variantsContent ?? (
146
114
  <EmptyState>No variant overview available.</EmptyState>
@@ -149,6 +117,8 @@ export function PrototypeReviewSidebar({
149
117
  <PrototypeChangeLogPanel onSelect={onSelect} selectedId={selectedId} />
150
118
  ) : !storageReady ? (
151
119
  <EmptyState>Loading comments…</EmptyState>
120
+ ) : storageConfigured === false ? (
121
+ <PrototypeCommentStorageEmptyState message={storageError ?? undefined} />
152
122
  ) : storageError ? (
153
123
  <EmptyState>{storageError}</EmptyState>
154
124
  ) : (
@@ -63,7 +63,7 @@ function CopyPrPrompt<TWireframeId extends string, TLiveState>({
63
63
  type="button"
64
64
  aria-label="Copy PR prompt"
65
65
  onClick={handleCopy}
66
- className="inline-flex size-[13px] shrink-0 items-center justify-center text-muted-foreground/60 transition-colors duration-200 ease hover:text-muted-foreground"
66
+ className="inline-flex size-[13px] shrink-0 cursor-pointer items-center justify-center text-muted-foreground/60 transition-colors duration-200 ease hover:text-muted-foreground"
67
67
  >
68
68
  {copied ? (
69
69
  <Check className="size-3 text-[var(--text-primary)]" />
@@ -227,7 +227,7 @@ function CopyPreviewLink<TWireframeId extends string, TLiveState>({
227
227
  : "Preview unavailable"
228
228
  }
229
229
  onClick={handleCopy}
230
- className="group inline-flex h-6 shrink-0 items-center gap-1 rounded-md border border-[var(--border-solid)] bg-[var(--bg-subtle)] px-2 text-[11px] text-[var(--text-secondary)] transition-colors duration-150 ease hover:bg-[var(--bg-layered)] hover:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-60"
230
+ className="group inline-flex h-6 shrink-0 cursor-pointer items-center gap-1 rounded-md border border-[var(--border-solid)] bg-[var(--bg-subtle)] px-2 text-[11px] text-[var(--text-secondary)] transition-colors duration-150 ease hover:bg-[var(--bg-layered)] hover:text-[var(--text-primary)] disabled:cursor-not-allowed disabled:opacity-60"
231
231
  >
232
232
  <Monitor
233
233
  className="size-3 shrink-0 text-[var(--text-tertiary)] transition-colors duration-150 ease group-hover:text-[var(--text-secondary)] group-disabled:opacity-60"
@@ -275,7 +275,7 @@ function PrStatusLink({
275
275
  rel="noopener noreferrer"
276
276
  aria-label={`Open pull request (${config.label})`}
277
277
  onClick={(e) => e.stopPropagation()}
278
- className={`inline-flex shrink-0 items-center gap-1.5 transition-opacity duration-200 ease hover:opacity-80 ${config.className}`}
278
+ className={`inline-flex shrink-0 cursor-pointer items-center gap-1.5 transition-opacity duration-200 ease hover:opacity-80 ${config.className}`}
279
279
  >
280
280
  {config.icon}
281
281
  <span className="text-[12px] font-medium">{config.label}</span>
@@ -337,7 +337,7 @@ function PrPreviewCard<TWireframeId extends string, TLiveState>({
337
337
  }}
338
338
  onMouseEnter={() => setHovered(true)}
339
339
  onMouseLeave={() => setHovered(false)}
340
- className="tool-review-card flex w-full flex-col overflow-hidden text-left"
340
+ className="tool-review-card flex w-full cursor-pointer flex-col overflow-hidden text-left"
341
341
  style={{
342
342
  borderRadius: 6,
343
343
  border: `1px solid ${reviewSidebarCardBorder(selected, hovered)}`,
@@ -392,7 +392,7 @@ function PrPreviewCard<TWireframeId extends string, TLiveState>({
392
392
  setExpanded((v) => !v);
393
393
  }
394
394
  }}
395
- className="inline-flex size-5 items-center justify-center rounded text-[var(--text-tertiary)] transition-colors duration-200 ease hover:text-[var(--text-secondary)]"
395
+ className="inline-flex size-5 cursor-pointer items-center justify-center rounded text-[var(--text-tertiary)] transition-colors duration-200 ease hover:text-[var(--text-secondary)]"
396
396
  >
397
397
  <ChevronDown
398
398
  className="size-4 transition-transform duration-200 ease"
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import { Button } from "@prototype/components/ui/button";
4
+ import { usePrototypeSlug } from "@prototype/components/prototypes/prototype-target";
5
+ import {
6
+ buildPrSplitPlanCopyText,
7
+ type PrSplitPlanPromptOptions,
8
+ } from "@prototype/lib/pr-split/build-pr-split-plan-prompt";
9
+ import useCopyToClipboard from "@prototype/lib/use-copy-to-clipboard";
10
+
11
+ type PrototypeSpecPanelEmptyStateProps = {
12
+ promptOptions?: Omit<PrSplitPlanPromptOptions, "slug">;
13
+ };
14
+
15
+ export function PrototypeSpecPanelEmptyState({
16
+ promptOptions,
17
+ }: PrototypeSpecPanelEmptyStateProps) {
18
+ const slug = usePrototypeSlug();
19
+ const { copy, icon, isCopied } = useCopyToClipboard();
20
+
21
+ const canCopy = Boolean(slug);
22
+
23
+ return (
24
+ <div className="px-1 py-2">
25
+ <p className="text-sm text-muted-foreground">
26
+ No PRs configured yet. Copy the prompt below to have an agent audit what
27
+ changed on this prototype and create a merge-ordered PR split spec.
28
+ </p>
29
+ <div className="mt-3">
30
+ <Button
31
+ type="button"
32
+ variant="outline"
33
+ size="sm"
34
+ className="h-8 cursor-pointer gap-1.5"
35
+ disabled={!canCopy}
36
+ onClick={() => {
37
+ if (!slug) return;
38
+ copy(
39
+ buildPrSplitPlanCopyText({
40
+ slug,
41
+ ...promptOptions,
42
+ }),
43
+ );
44
+ }}
45
+ aria-label="Copy PR split planning prompt"
46
+ >
47
+ {isCopied ? "Copied prompt" : "Copy planning prompt"}
48
+ {icon}
49
+ </Button>
50
+ </div>
51
+ </div>
52
+ );
53
+ }
@@ -1,19 +1,21 @@
1
- $canvas-chrome-bg: var(--tool-chrome-bg);
2
- $canvas-chrome-muted: var(--tool-chrome-surface-muted, #252525);
1
+ $canvas-chrome-bg: var(--prototype-state-map-canvas-bg, var(--tool-chrome-bg));
2
+ $canvas-chrome-muted: var(--tool-chrome-surface-muted, #fafafa);
3
3
  $canvas-border: var(--tool-chrome-border);
4
4
  $canvas-border-strong: var(--tool-chrome-border-strong);
5
5
  $canvas-text: var(--tool-chrome-text);
6
6
  $canvas-text-muted: var(--tool-chrome-text-muted);
7
- $canvas-text-tertiary: var(--tool-chrome-icon-hover, #a3a3a3);
8
- $canvas-edge-base: var(--prototype-state-canvas-edge, var(--tool-chrome-icon-hover, #a3a3a3));
7
+ $canvas-text-tertiary: var(--tool-chrome-icon-hover, rgba(0, 0, 0, 0.45));
8
+ $canvas-edge-base: var(--prototype-state-canvas-edge, rgba(0, 0, 0, 0.22));
9
9
  // Pre-blend to canvas bg so edges stay subtle without semi-transparent layering at corners/arrowheads.
10
10
  $canvas-edge-color: color-mix(in srgb, $canvas-edge-base 50%, $canvas-chrome-bg);
11
11
 
12
- $wireframe-bg: #ffffff;
13
- $wireframe-subtle: var(--color-gray-50);
14
- $wireframe-border: #edebeb;
15
- $wireframe-border-medium: var(--color-gray-300);
16
- $wireframe-text: var(--color-gray-900);
12
+ $wireframe-bg: var(--prototype-state-map-node-surface, var(--tool-chrome-surface));
13
+ $wireframe-subtle: var(--prototype-state-map-node-preview-bg, var(--tool-chrome-surface-muted));
14
+ $wireframe-border: var(--tool-chrome-border);
15
+ $wireframe-border-medium: var(--tool-chrome-border-strong);
16
+ $wireframe-footer-bg: var(--prototype-state-map-node-footer-bg, var(--tool-chrome-surface-muted));
17
+ $wireframe-footer-border: var(--prototype-state-map-node-footer-border, var(--tool-chrome-border-strong));
18
+ $wireframe-footer-text: var(--prototype-state-map-node-footer-text, var(--tool-chrome-text-heading));
17
19
 
18
20
  .page {
19
21
  display: flex;
@@ -193,10 +195,6 @@ $wireframe-text: var(--color-gray-900);
193
195
  }
194
196
  }
195
197
 
196
- .nodeGroupSelected {
197
- border-color: color-mix(in srgb, var(--tool-chrome-blue) 55%, transparent);
198
- }
199
-
200
198
  .node {
201
199
  display: flex;
202
200
  flex-direction: column;
@@ -224,20 +222,9 @@ $wireframe-text: var(--color-gray-900);
224
222
  }
225
223
  }
226
224
 
227
- .nodeSelected {
228
- background: color-mix(in srgb, var(--tool-chrome-blue) 6%, $wireframe-bg);
229
-
230
- @media (hover: hover) and (pointer: fine) {
231
- .nodeGroup:hover & {
232
- background: color-mix(in srgb, var(--tool-chrome-blue) 8%, $wireframe-bg);
233
- }
234
- }
235
- }
236
-
237
225
  .nodePreview {
238
226
  height: 132px;
239
227
  overflow: hidden;
240
- border-bottom: 1px solid $wireframe-border;
241
228
  background: $wireframe-subtle;
242
229
  pointer-events: none;
243
230
  }
@@ -253,20 +240,16 @@ $wireframe-text: var(--color-gray-900);
253
240
  align-items: center;
254
241
  justify-content: space-between;
255
242
  gap: 0.5rem;
256
- min-height: 36px;
257
- padding: 0 0.625rem;
258
- background: $wireframe-bg;
243
+ min-height: 40px;
244
+ padding: 0 0.75rem;
245
+ border-top: 1px solid $wireframe-footer-border;
246
+ background: $wireframe-footer-bg;
259
247
  }
260
248
 
261
249
  .nodeLabelText {
262
250
  font-size: 0.75rem;
263
- font-weight: 500;
264
- color: $wireframe-text;
265
- }
266
-
267
- .nodeSelectedIcon {
268
- flex-shrink: 0;
269
- color: var(--tool-chrome-blue);
251
+ font-weight: 600;
252
+ color: $wireframe-footer-text;
270
253
  }
271
254
 
272
255
  .calloutTray {
@@ -297,7 +280,7 @@ $wireframe-text: var(--color-gray-900);
297
280
  @media (hover: hover) and (pointer: fine) {
298
281
  &:hover {
299
282
  border-color: $wireframe-border-medium;
300
- background: color-mix(in srgb, var(--color-gray-900) 4%, $wireframe-bg);
283
+ background: var(--tool-chrome-gray-highlight);
301
284
  }
302
285
  }
303
286
 
@@ -311,26 +294,16 @@ $wireframe-text: var(--color-gray-900);
311
294
  }
312
295
  }
313
296
 
314
- .calloutSelected {
315
- border-color: color-mix(in srgb, var(--tool-chrome-blue) 45%, transparent);
316
- background: color-mix(in srgb, var(--tool-chrome-blue) 6%, $wireframe-bg);
317
- }
318
-
319
297
  .calloutLabel {
320
298
  flex: 1 1 auto;
321
299
  min-width: 0;
322
300
  font-size: 0.6875rem;
323
301
  font-weight: 500;
324
- color: $wireframe-text;
302
+ color: $wireframe-footer-text;
325
303
  }
326
304
 
327
305
  .calloutHint {
328
306
  flex-shrink: 0;
329
307
  font-size: 0.625rem;
330
- color: var(--color-gray-500);
331
- }
332
-
333
- .calloutSelectedIcon {
334
- flex-shrink: 0;
335
- color: var(--tool-chrome-blue);
308
+ color: var(--tool-chrome-text-muted);
336
309
  }
@@ -13,7 +13,7 @@ import {
13
13
  getStateCanvasNodeHeight,
14
14
  } from "@prototype/lib/prototypes/prototype-state-canvas-constants";
15
15
  import { cn } from "@prototype/lib/utils";
16
- import { Check, Maximize2, Minus, Plus, X } from "lucide-react";
16
+ import { Maximize2, Minus, Plus, X } from "lucide-react";
17
17
  import Link from "next/link";
18
18
  import {
19
19
  useCallback,
@@ -285,15 +285,6 @@ function deriveCanvasSize<T extends string>(
285
285
  };
286
286
  }
287
287
 
288
- function isNodeActive<T extends string>(
289
- node: PrototypeStateCanvasNode<T>,
290
- activeStateId: T | undefined,
291
- ): boolean {
292
- if (activeStateId == null) return false;
293
- if (node.id === activeStateId) return true;
294
- return node.callouts?.some((callout) => callout.id === activeStateId) ?? false;
295
- }
296
-
297
288
  type HoverAnnotationSource<T extends string> = {
298
289
  id: T;
299
290
  label: string;
@@ -354,7 +345,6 @@ export function PrototypeStateCanvasView<T extends string>({
354
345
  onClose,
355
346
  }: PrototypeStateCanvasViewProps<T>) {
356
347
  const {
357
- activeStateId,
358
348
  onStateSelect,
359
349
  nodes,
360
350
  edges,
@@ -877,7 +867,6 @@ export function PrototypeStateCanvasView<T extends string>({
877
867
  ))}
878
868
 
879
869
  {nodes.map((node) => {
880
- const selected = isNodeActive(node, activeStateId);
881
870
  const hoverSource = resolveHoverAnnotationSource(node, hoverTarget);
882
871
  const previewNodeId = resolveNodePreviewId(node, hoverTarget);
883
872
  const previewHighlights = getHighlightRegions?.(previewNodeId);
@@ -893,18 +882,12 @@ export function PrototypeStateCanvasView<T extends string>({
893
882
  return (
894
883
  <div
895
884
  key={node.id}
896
- className={cn(styles.nodeGroup, selected && styles.nodeGroupSelected)}
885
+ className={styles.nodeGroup}
897
886
  style={{ left: node.x, top: node.y, width: NODE_WIDTH }}
898
887
  >
899
888
  <button
900
889
  type="button"
901
- className={cn(
902
- styles.node,
903
- selected && node.id === activeStateId && styles.nodeSelected,
904
- )}
905
- aria-current={
906
- node.id === activeStateId ? "true" : undefined
907
- }
890
+ className={styles.node}
908
891
  onPointerDown={(event) => event.stopPropagation()}
909
892
  onClick={() => handleNodeSelect(node.id)}
910
893
  onMouseEnter={() => {
@@ -934,30 +917,17 @@ export function PrototypeStateCanvasView<T extends string>({
934
917
  </div>
935
918
  <div className={styles.nodeLabel}>
936
919
  <span className={styles.nodeLabelText}>{node.label}</span>
937
- {node.id === activeStateId ? (
938
- <Check
939
- size={14}
940
- strokeWidth={2}
941
- className={styles.nodeSelectedIcon}
942
- aria-hidden
943
- />
944
- ) : null}
945
920
  </div>
946
921
  </button>
947
922
 
948
923
  {node.callouts?.length ? (
949
924
  <div className={styles.calloutTray}>
950
925
  {node.callouts.map((callout) => {
951
- const calloutSelected = callout.id === activeStateId;
952
926
  return (
953
927
  <button
954
928
  key={callout.id}
955
929
  type="button"
956
- className={cn(
957
- styles.callout,
958
- calloutSelected && styles.calloutSelected,
959
- )}
960
- aria-current={calloutSelected ? "true" : undefined}
930
+ className={styles.callout}
961
931
  onPointerDown={(event) => event.stopPropagation()}
962
932
  onClick={() => handleNodeSelect(callout.id)}
963
933
  onMouseEnter={() => {
@@ -981,14 +951,6 @@ export function PrototypeStateCanvasView<T extends string>({
981
951
  {callout.hint}
982
952
  </span>
983
953
  ) : null}
984
- {calloutSelected ? (
985
- <Check
986
- size={12}
987
- strokeWidth={2}
988
- className={styles.calloutSelectedIcon}
989
- aria-hidden
990
- />
991
- ) : null}
992
954
  </button>
993
955
  );
994
956
  })}