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.
- package/AGENTS.md +308 -29
- package/README.md +55 -13
- package/package.json +1 -1
- package/scripts/lib/host-config.example.sh +1 -1
- package/scripts/publish-package.sh +41 -0
- package/scripts/verify-prototype-preview-states.mjs +57 -0
- package/src/components/platform-ui/controls-panel-select.tsx +4 -3
- package/src/components/platform-ui/prototype-tool-dialog.tsx +117 -0
- package/src/components/platform-ui/sidebar.tsx +34 -16
- package/src/components/prototype-gallery-client.tsx +77 -37
- package/src/components/prototypes/design-exploration-variant-preview-shell.tsx +43 -0
- package/src/components/prototypes/prototype-brief-field.tsx +33 -0
- package/src/components/prototypes/prototype-change-log-panel.tsx +4 -0
- package/src/components/prototypes/prototype-comment-storage-empty-state.tsx +41 -0
- package/src/components/prototypes/prototype-create-exploration-modal.tsx +41 -64
- package/src/components/prototypes/prototype-create-prototype-modal.tsx +102 -0
- package/src/components/prototypes/prototype-create-state-modal.tsx +31 -70
- package/src/components/prototypes/prototype-default-state-map-page.tsx +4 -4
- package/src/components/prototypes/prototype-design-brief-modal.tsx +10 -10
- package/src/components/prototypes/prototype-design-brief-panel.tsx +261 -231
- package/src/components/prototypes/prototype-floating-pill.module.scss +16 -9
- package/src/components/prototypes/prototype-mobbin-gallery.tsx +18 -2
- package/src/components/prototypes/prototype-review-sidebar.tsx +11 -28
- package/src/components/prototypes/prototype-spec-panel-content.tsx +5 -5
- package/src/components/prototypes/prototype-spec-panel-empty-state.tsx +53 -0
- package/src/components/prototypes/prototype-state-canvas-overlay.module.scss +20 -47
- package/src/components/prototypes/prototype-state-canvas-overlay.tsx +4 -42
- package/src/components/prototypes/prototype-variant-explorer.tsx +32 -78
- package/src/components/prototypes/prototype-vercel-preview-modal.tsx +4 -3
- package/src/components/prototypes/state-map-annotation-tooltip.module.scss +7 -7
- package/src/components/shell/prototype-component-library-empty-state.tsx +59 -0
- package/src/components/shell/prototype-gallery-nav.tsx +124 -0
- package/src/components/shell/prototype-gallery-shell.tsx +45 -13
- package/src/components/shell/prototype-link-source-modal.tsx +100 -0
- package/src/components/ui/button.tsx +3 -1
- package/src/components/ui/dialog.tsx +43 -4
- package/src/config/create-prototype-component-library-page.tsx +74 -0
- package/src/config/create-prototype-gallery-page.tsx +26 -23
- package/src/config/create-prototype-host-app.tsx +3 -15
- package/src/config/create-prototype-site-layout.tsx +14 -1
- package/src/config/create-prototype-state-map-page.tsx +44 -0
- package/src/config/prototype-site-layout-client.tsx +20 -6
- package/src/config/with-prototype.ts +21 -0
- package/src/index.ts +54 -10
- package/src/lib/copy-text-with-attachments.ts +35 -0
- package/src/lib/format-source-directory-name.ts +12 -0
- package/src/lib/platform-ui/sidebar-interactive-cursor.ts +19 -0
- package/src/lib/pr-split/build-pr-split-plan-prompt.ts +81 -0
- package/src/lib/pr-split/pr-split-highlight.ts +3 -0
- package/src/lib/prototype-comments/core/annotation-target.ts +12 -3
- package/src/lib/prototype-comments/core/comment-capture-blocked.ts +24 -0
- package/src/lib/prototype-comments/core/screenshot.ts +68 -31
- package/src/lib/prototype-comments/core/storage.ts +3 -6
- package/src/lib/prototype-comments/core/types.ts +2 -0
- package/src/lib/prototype-comments/hooks/useLayoutShift.ts +9 -2
- package/src/lib/prototype-comments/react/CommentCaptureToolbar.tsx +45 -10
- package/src/lib/prototype-comments/react/CommentProvider.tsx +19 -0
- package/src/lib/prototype-comments/ui/CommentModeToggleButton.tsx +34 -0
- package/src/lib/prototype-comments/ui/CommentsGrid.tsx +8 -16
- package/src/lib/prototype-comments/ui/CommentsSidebar.tsx +5 -19
- package/src/lib/prototype-comments/ui/capture-styles.module.scss +5 -1
- package/src/lib/prototype-comments/ui/comments-grid-card.module.scss +0 -7
- package/src/lib/prototypes/build-design-exploration-renderers.ts +44 -0
- package/src/lib/prototypes/comment-storage-setup-prompt.ts +124 -0
- package/src/lib/prototypes/create-prototype-prompt.ts +96 -0
- package/src/lib/prototypes/create-prototype-registry.ts +5 -0
- package/src/lib/prototypes/design-exploration-types.ts +261 -10
- package/src/lib/prototypes/link-source-prompt.ts +93 -0
- package/src/lib/prototypes/populate-component-library-prompt.ts +64 -0
- package/src/lib/prototypes/prototype-config-types.ts +2 -0
- package/src/lib/prototypes/prototype-layout-classes.ts +19 -0
- package/src/lib/prototypes/prototype-preview-state-registry.ts +5 -2
- package/src/lib/prototypes/prototype-review-context.tsx +4 -4
- package/src/lib/prototypes/prototype-state-canvas-constants.ts +1 -1
- package/src/lib/prototypes/prototype-state-canvas-types.ts +0 -2
- package/src/lib/prototypes/prototype-storage-status.ts +31 -0
- package/src/lib/prototypes/sort-prototypes-by-creation-date.ts +43 -0
- package/src/lib/prototypes/use-prototype-tool-theme.tsx +1 -1
- package/src/lib/prototypes/use-toolbar-hover-panel.ts +12 -1
- package/src/lib/prototypes/variant-set-lucide-icon.tsx +24 -0
- package/src/lib/tool-portal.ts +24 -0
- package/src/lib/use-copy-to-clipboard.ts +7 -13
- package/src/server/create-host-api-route.ts +7 -16
- package/src/server/create-prototype-api-routes.ts +0 -115
- package/src/server/prototype-storage-status-route.ts +29 -0
- package/src/server.ts +0 -8
- package/src/styles/globals.css +359 -71
- package/src/lib/prototypes/design-exploration-default-storage.ts +0 -68
- package/src/lib/prototypes/design-exploration-default.ts +0 -51
- package/src/lib/prototypes/use-design-exploration-default.ts +0 -114
- 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
|
-
|
|
159
|
+
const lightbox = (
|
|
149
160
|
<PrototypeComponent
|
|
150
161
|
id={`${galleryId}.lightbox`}
|
|
151
|
-
className="fixed inset-0
|
|
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
|
-
|
|
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
|
-
<
|
|
100
|
-
|
|
96
|
+
<CommentModeToggleButton
|
|
97
|
+
isActive={isCommentModeActive}
|
|
101
98
|
onClick={onToggleCommentMode}
|
|
102
|
-
|
|
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, #
|
|
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,
|
|
8
|
-
$canvas-edge-base: var(--prototype-state-canvas-edge,
|
|
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:
|
|
13
|
-
$wireframe-subtle: var(--
|
|
14
|
-
$wireframe-border:
|
|
15
|
-
$wireframe-border-medium: var(--
|
|
16
|
-
$wireframe-
|
|
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:
|
|
257
|
-
padding: 0 0.
|
|
258
|
-
|
|
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:
|
|
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:
|
|
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(--
|
|
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 {
|
|
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={
|
|
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={
|
|
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={
|
|
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
|
})}
|