proto-plugin 0.1.0 → 0.1.2

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 (91) hide show
  1. package/AGENTS.md +308 -29
  2. package/README.md +55 -13
  3. package/package.json +1 -1
  4. package/scripts/lib/host-config.example.sh +1 -1
  5. package/scripts/publish-package.sh +41 -0
  6. package/scripts/verify-prototype-preview-states.mjs +57 -0
  7. package/src/components/platform-ui/controls-panel-select.tsx +4 -3
  8. package/src/components/platform-ui/prototype-tool-dialog.tsx +117 -0
  9. package/src/components/platform-ui/sidebar.tsx +34 -16
  10. package/src/components/prototype-gallery-client.tsx +77 -37
  11. package/src/components/prototypes/design-exploration-variant-preview-shell.tsx +43 -0
  12. package/src/components/prototypes/prototype-brief-field.tsx +33 -0
  13. package/src/components/prototypes/prototype-change-log-panel.tsx +4 -0
  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-sidebar.tsx +11 -28
  24. package/src/components/prototypes/prototype-spec-panel-content.tsx +5 -5
  25. package/src/components/prototypes/prototype-spec-panel-empty-state.tsx +53 -0
  26. package/src/components/prototypes/prototype-state-canvas-overlay.module.scss +20 -47
  27. package/src/components/prototypes/prototype-state-canvas-overlay.tsx +4 -42
  28. package/src/components/prototypes/prototype-variant-explorer.tsx +32 -78
  29. package/src/components/prototypes/prototype-vercel-preview-modal.tsx +4 -3
  30. package/src/components/prototypes/state-map-annotation-tooltip.module.scss +7 -7
  31. package/src/components/shell/prototype-component-library-empty-state.tsx +59 -0
  32. package/src/components/shell/prototype-gallery-nav.tsx +124 -0
  33. package/src/components/shell/prototype-gallery-shell.tsx +45 -13
  34. package/src/components/shell/prototype-link-source-modal.tsx +100 -0
  35. package/src/components/ui/button.tsx +3 -1
  36. package/src/components/ui/dialog.tsx +43 -4
  37. package/src/config/create-prototype-component-library-page.tsx +74 -0
  38. package/src/config/create-prototype-gallery-page.tsx +26 -23
  39. package/src/config/create-prototype-host-app.tsx +3 -15
  40. package/src/config/create-prototype-site-layout.tsx +14 -1
  41. package/src/config/create-prototype-state-map-page.tsx +44 -0
  42. package/src/config/prototype-site-layout-client.tsx +20 -6
  43. package/src/config/with-prototype.ts +21 -0
  44. package/src/index.ts +54 -10
  45. package/src/lib/copy-text-with-attachments.ts +35 -0
  46. package/src/lib/format-source-directory-name.ts +12 -0
  47. package/src/lib/platform-ui/sidebar-interactive-cursor.ts +19 -0
  48. package/src/lib/pr-split/build-pr-split-plan-prompt.ts +81 -0
  49. package/src/lib/pr-split/pr-split-highlight.ts +3 -0
  50. package/src/lib/prototype-comments/core/annotation-target.ts +12 -3
  51. package/src/lib/prototype-comments/core/comment-capture-blocked.ts +24 -0
  52. package/src/lib/prototype-comments/core/screenshot.ts +68 -31
  53. package/src/lib/prototype-comments/core/storage.ts +3 -6
  54. package/src/lib/prototype-comments/core/types.ts +2 -0
  55. package/src/lib/prototype-comments/hooks/useLayoutShift.ts +9 -2
  56. package/src/lib/prototype-comments/react/CommentCaptureToolbar.tsx +45 -10
  57. package/src/lib/prototype-comments/react/CommentProvider.tsx +19 -0
  58. package/src/lib/prototype-comments/ui/CommentModeToggleButton.tsx +34 -0
  59. package/src/lib/prototype-comments/ui/CommentsGrid.tsx +8 -16
  60. package/src/lib/prototype-comments/ui/CommentsSidebar.tsx +5 -19
  61. package/src/lib/prototype-comments/ui/capture-styles.module.scss +5 -1
  62. package/src/lib/prototype-comments/ui/comments-grid-card.module.scss +0 -7
  63. package/src/lib/prototypes/build-design-exploration-renderers.ts +44 -0
  64. package/src/lib/prototypes/comment-storage-setup-prompt.ts +124 -0
  65. package/src/lib/prototypes/create-prototype-prompt.ts +96 -0
  66. package/src/lib/prototypes/create-prototype-registry.ts +5 -0
  67. package/src/lib/prototypes/design-exploration-types.ts +261 -10
  68. package/src/lib/prototypes/link-source-prompt.ts +93 -0
  69. package/src/lib/prototypes/populate-component-library-prompt.ts +64 -0
  70. package/src/lib/prototypes/prototype-config-types.ts +2 -0
  71. package/src/lib/prototypes/prototype-layout-classes.ts +19 -0
  72. package/src/lib/prototypes/prototype-preview-state-registry.ts +5 -2
  73. package/src/lib/prototypes/prototype-review-context.tsx +4 -4
  74. package/src/lib/prototypes/prototype-state-canvas-constants.ts +1 -1
  75. package/src/lib/prototypes/prototype-state-canvas-types.ts +0 -2
  76. package/src/lib/prototypes/prototype-storage-status.ts +31 -0
  77. package/src/lib/prototypes/sort-prototypes-by-creation-date.ts +43 -0
  78. package/src/lib/prototypes/use-prototype-tool-theme.tsx +1 -1
  79. package/src/lib/prototypes/use-toolbar-hover-panel.ts +12 -1
  80. package/src/lib/prototypes/variant-set-lucide-icon.tsx +24 -0
  81. package/src/lib/tool-portal.ts +24 -0
  82. package/src/lib/use-copy-to-clipboard.ts +7 -13
  83. package/src/server/create-host-api-route.ts +7 -16
  84. package/src/server/create-prototype-api-routes.ts +0 -115
  85. package/src/server/prototype-storage-status-route.ts +29 -0
  86. package/src/server.ts +0 -8
  87. package/src/styles/globals.css +359 -71
  88. package/src/lib/prototypes/design-exploration-default-storage.ts +0 -68
  89. package/src/lib/prototypes/design-exploration-default.ts +0 -51
  90. package/src/lib/prototypes/use-design-exploration-default.ts +0 -114
  91. 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({
@@ -7,16 +7,13 @@ 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 { CommentModeToggleButton } from "@prototype/lib/prototype-comments/ui/CommentModeToggleButton";
11
+ import { Sidebar } from "@prototype/components/platform-ui/sidebar";
16
12
  import { EmptyState } from "@prototype/components/platform-ui/empty-state";
17
13
  import { PrototypeChangeLogPanel } from "@prototype/components/prototypes/prototype-change-log-panel";
14
+ import { PrototypeCommentStorageEmptyState } from "@prototype/components/prototypes/prototype-comment-storage-empty-state";
15
+ import { PrototypeSpecPanelEmptyState } from "@prototype/components/prototypes/prototype-spec-panel-empty-state";
18
16
  import { miniPillTextFromLabel } from "@prototype/components/prototypes/mini-pill-label";
19
- import { MessageSquarePlus } from "lucide-react";
20
17
  import { useMemo } from "react";
21
18
 
22
19
  type PrototypeReviewSidebarProps = {
@@ -35,7 +32,7 @@ export function PrototypeReviewSidebar({
35
32
  onToggleCommentMode,
36
33
  }: PrototypeReviewSidebarProps) {
37
34
  const review = usePrototypeReview();
38
- const { annotations, deleteAnnotation, updateAnnotation, resolveAnnotation, storageError, storageReady } = useCommentStore();
35
+ const { annotations, deleteAnnotation, updateAnnotation, resolveAnnotation, storageError, storageReady, storageConfigured } = useCommentStore();
39
36
  const isVariantsPanel = review.sidebarPanel === "variants";
40
37
  const isChangeLogPanel = review.sidebarPanel === "change-log";
41
38
  const isSpecPanel = review.sidebarPanel === "spec";
@@ -96,21 +93,10 @@ export function PrototypeReviewSidebar({
96
93
  }
97
94
  headerActions={
98
95
  isCommentsPanel && onToggleCommentMode ? (
99
- <IconButton
100
- type="button"
96
+ <CommentModeToggleButton
97
+ isActive={isCommentModeActive}
101
98
  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>
99
+ />
114
100
  ) : undefined
115
101
  }
116
102
  onClose={
@@ -135,12 +121,7 @@ export function PrototypeReviewSidebar({
135
121
  widthCssVar="--comments-sidebar-width"
136
122
  >
137
123
  {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
- )
124
+ specContent ?? <PrototypeSpecPanelEmptyState />
144
125
  ) : isVariantsPanel ? (
145
126
  variantsContent ?? (
146
127
  <EmptyState>No variant overview available.</EmptyState>
@@ -149,6 +130,8 @@ export function PrototypeReviewSidebar({
149
130
  <PrototypeChangeLogPanel onSelect={onSelect} selectedId={selectedId} />
150
131
  ) : !storageReady ? (
151
132
  <EmptyState>Loading comments…</EmptyState>
133
+ ) : storageConfigured === false ? (
134
+ <PrototypeCommentStorageEmptyState message={storageError ?? undefined} />
152
135
  ) : storageError ? (
153
136
  <EmptyState>{storageError}</EmptyState>
154
137
  ) : (
@@ -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
  })}