proto-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/AGENTS.md +350 -0
  2. package/README.md +279 -0
  3. package/components.json +18 -0
  4. package/package.json +73 -0
  5. package/scripts/download-mobbin-reference-images.mjs +112 -0
  6. package/scripts/lib/host-config.example.sh +48 -0
  7. package/scripts/lib/host-config.sh +25 -0
  8. package/scripts/lib/resolve-host-root.mjs +18 -0
  9. package/scripts/lib/source-path.sh +49 -0
  10. package/scripts/link-source-db.sh +128 -0
  11. package/scripts/link-source.sh +40 -0
  12. package/scripts/publish-package.sh +147 -0
  13. package/scripts/share-prototype.sh +279 -0
  14. package/scripts/sync-from-source.sh +74 -0
  15. package/scripts/verify-prototype-component-ids.mjs +151 -0
  16. package/scripts/verify-prototype-preview-states.mjs +135 -0
  17. package/src/app.ts +4 -0
  18. package/src/components/platform-ui/code-block.tsx +21 -0
  19. package/src/components/platform-ui/controls-panel-options.tsx +71 -0
  20. package/src/components/platform-ui/controls-panel-select.tsx +438 -0
  21. package/src/components/platform-ui/empty-state.tsx +15 -0
  22. package/src/components/platform-ui/icon-button.tsx +18 -0
  23. package/src/components/platform-ui/panel-select.tsx +123 -0
  24. package/src/components/platform-ui/panel.tsx +97 -0
  25. package/src/components/platform-ui/review-dropdown-menu.tsx +35 -0
  26. package/src/components/platform-ui/sidebar.tsx +293 -0
  27. package/src/components/platform-ui/toolbar-icon-button.tsx +34 -0
  28. package/src/components/platform-ui/toolbar.tsx +37 -0
  29. package/src/components/prototype-gallery-client.tsx +57 -0
  30. package/src/components/prototype-provider.tsx +20 -0
  31. package/src/components/prototypes/mini-pill-label.tsx +37 -0
  32. package/src/components/prototypes/prototype-change-log-panel.tsx +207 -0
  33. package/src/components/prototypes/prototype-comment-provider.tsx +61 -0
  34. package/src/components/prototypes/prototype-comment-toolbar.tsx +65 -0
  35. package/src/components/prototypes/prototype-control.tsx +31 -0
  36. package/src/components/prototypes/prototype-controls.tsx +553 -0
  37. package/src/components/prototypes/prototype-create-exploration-modal.tsx +117 -0
  38. package/src/components/prototypes/prototype-create-state-modal.tsx +117 -0
  39. package/src/components/prototypes/prototype-default-state-map-page.tsx +66 -0
  40. package/src/components/prototypes/prototype-design-brief-modal.tsx +128 -0
  41. package/src/components/prototypes/prototype-design-brief-panel.tsx +525 -0
  42. package/src/components/prototypes/prototype-design-context-panel.tsx +65 -0
  43. package/src/components/prototypes/prototype-floating-pill.module.scss +1341 -0
  44. package/src/components/prototypes/prototype-mobbin-gallery.tsx +330 -0
  45. package/src/components/prototypes/prototype-reference-docs.tsx +222 -0
  46. package/src/components/prototypes/prototype-review-chrome.module.scss +630 -0
  47. package/src/components/prototypes/prototype-review-chrome.tsx +1241 -0
  48. package/src/components/prototypes/prototype-review-dropdown-menu-item.tsx +1 -0
  49. package/src/components/prototypes/prototype-review-dropdown-menu-separator.tsx +1 -0
  50. package/src/components/prototypes/prototype-review-panel-menu-item.tsx +1 -0
  51. package/src/components/prototypes/prototype-review-panel-menu-separator.tsx +1 -0
  52. package/src/components/prototypes/prototype-review-panel-select.tsx +4 -0
  53. package/src/components/prototypes/prototype-review-sidebar.tsx +167 -0
  54. package/src/components/prototypes/prototype-share-link-restore.tsx +75 -0
  55. package/src/components/prototypes/prototype-share-mode.tsx +206 -0
  56. package/src/components/prototypes/prototype-spec-panel-content.tsx +538 -0
  57. package/src/components/prototypes/prototype-state-canvas-overlay.module.scss +336 -0
  58. package/src/components/prototypes/prototype-state-canvas-overlay.tsx +1023 -0
  59. package/src/components/prototypes/prototype-state-canvas.tsx +38 -0
  60. package/src/components/prototypes/prototype-state-screenshot-preview.tsx +47 -0
  61. package/src/components/prototypes/prototype-target.tsx +120 -0
  62. package/src/components/prototypes/prototype-tweak-highlight.tsx +132 -0
  63. package/src/components/prototypes/prototype-tweaks.tsx +48 -0
  64. package/src/components/prototypes/prototype-variant-explorer.tsx +347 -0
  65. package/src/components/prototypes/prototype-variant-rationale-panel.tsx +49 -0
  66. package/src/components/prototypes/prototype-variant-tabs.tsx +207 -0
  67. package/src/components/prototypes/prototype-vercel-preview-modal.tsx +120 -0
  68. package/src/components/prototypes/state-map-annotation-tooltip.module.scss +89 -0
  69. package/src/components/prototypes/state-map-annotation-tooltip.tsx +137 -0
  70. package/src/components/prototypes/state-map-wireframe-highlight.module.scss +25 -0
  71. package/src/components/prototypes/state-map-wireframe-highlight.tsx +82 -0
  72. package/src/components/shell/prototype-gallery-shell.tsx +79 -0
  73. package/src/components/shell/prototype-screenshot-capture.tsx +45 -0
  74. package/src/components/shell/prototype-shell.module.scss +194 -0
  75. package/src/components/shell/prototype-shell.tsx +160 -0
  76. package/src/components/shell/prototype-state-screenshot-capture.tsx +68 -0
  77. package/src/components/ui/button.tsx +58 -0
  78. package/src/components/ui/dialog.module.scss +81 -0
  79. package/src/components/ui/dialog.tsx +127 -0
  80. package/src/components/ui/dropdown-menu.tsx +200 -0
  81. package/src/components/ui/input.tsx +22 -0
  82. package/src/components/ui/label.tsx +26 -0
  83. package/src/components/ui/option-select.tsx +81 -0
  84. package/src/components/ui/scroll-area.tsx +48 -0
  85. package/src/components/ui/separator.tsx +31 -0
  86. package/src/components/ui/slider.tsx +28 -0
  87. package/src/components/ui/switch.tsx +29 -0
  88. package/src/components/ui/tabs.tsx +55 -0
  89. package/src/components/ui/toggle-group.tsx +80 -0
  90. package/src/components/ui/tooltip.tsx +32 -0
  91. package/src/config/create-prototype-gallery-page.tsx +65 -0
  92. package/src/config/create-prototype-host-app.tsx +121 -0
  93. package/src/config/create-prototype-page.tsx +41 -0
  94. package/src/config/create-prototype-site-layout.tsx +28 -0
  95. package/src/config/define-prototype-config.ts +7 -0
  96. package/src/config/prototype-icon.tsx +25 -0
  97. package/src/config/prototype-site-layout-client.tsx +35 -0
  98. package/src/config/with-prototype.ts +20 -0
  99. package/src/index.ts +303 -0
  100. package/src/lib/pr-split/build-pr-agent-prompt.ts +68 -0
  101. package/src/lib/pr-split/build-pr-split-prototype-url.ts +12 -0
  102. package/src/lib/pr-split/pr-split-highlight.ts +6 -0
  103. package/src/lib/pr-split/pr-split-types.ts +58 -0
  104. package/src/lib/pr-split/resolve-source-preview-url.ts +45 -0
  105. package/src/lib/pr-split/use-pr-vercel-previews.ts +69 -0
  106. package/src/lib/prototype-comments/core/annotation-channel.ts +41 -0
  107. package/src/lib/prototype-comments/core/annotation-status.ts +60 -0
  108. package/src/lib/prototype-comments/core/annotation-target.ts +567 -0
  109. package/src/lib/prototype-comments/core/capture-theme.ts +41 -0
  110. package/src/lib/prototype-comments/core/capture.ts +11 -0
  111. package/src/lib/prototype-comments/core/comment-capture-blocked.ts +47 -0
  112. package/src/lib/prototype-comments/core/comment-threads.ts +52 -0
  113. package/src/lib/prototype-comments/core/element-identification.ts +615 -0
  114. package/src/lib/prototype-comments/core/export.ts +35 -0
  115. package/src/lib/prototype-comments/core/freeze-animations.ts +266 -0
  116. package/src/lib/prototype-comments/core/identify-element.ts +76 -0
  117. package/src/lib/prototype-comments/core/normalize-viewport.ts +35 -0
  118. package/src/lib/prototype-comments/core/react-detection.ts +704 -0
  119. package/src/lib/prototype-comments/core/restore.ts +7 -0
  120. package/src/lib/prototype-comments/core/screenshot.ts +428 -0
  121. package/src/lib/prototype-comments/core/source-location.ts +906 -0
  122. package/src/lib/prototype-comments/core/storage.ts +80 -0
  123. package/src/lib/prototype-comments/core/types.ts +79 -0
  124. package/src/lib/prototype-comments/core/validation.ts +17 -0
  125. package/src/lib/prototype-comments/hooks/useLayoutShift.ts +61 -0
  126. package/src/lib/prototype-comments/index.ts +58 -0
  127. package/src/lib/prototype-comments/react/CommentCaptureToolbar.tsx +1398 -0
  128. package/src/lib/prototype-comments/react/CommentProvider.tsx +298 -0
  129. package/src/lib/prototype-comments/scss.d.ts +10 -0
  130. package/src/lib/prototype-comments/ui/CommentAnnotationOverlay.tsx +253 -0
  131. package/src/lib/prototype-comments/ui/CommentDetailPage.tsx +56 -0
  132. package/src/lib/prototype-comments/ui/CommentHeaderCount.tsx +28 -0
  133. package/src/lib/prototype-comments/ui/CommentThread.tsx +331 -0
  134. package/src/lib/prototype-comments/ui/CommentsGrid.tsx +780 -0
  135. package/src/lib/prototype-comments/ui/CommentsListPage.tsx +25 -0
  136. package/src/lib/prototype-comments/ui/CommentsSidebar.tsx +77 -0
  137. package/src/lib/prototype-comments/ui/annotation-marker/index.tsx +200 -0
  138. package/src/lib/prototype-comments/ui/annotation-marker/styles.module.scss +194 -0
  139. package/src/lib/prototype-comments/ui/annotation-popup/index.tsx +335 -0
  140. package/src/lib/prototype-comments/ui/annotation-popup/styles.module.scss +469 -0
  141. package/src/lib/prototype-comments/ui/capture-styles.module.scss +2225 -0
  142. package/src/lib/prototype-comments/ui/comment-highlight.ts +48 -0
  143. package/src/lib/prototype-comments/ui/comment-thread.module.scss +203 -0
  144. package/src/lib/prototype-comments/ui/comments-grid-card.module.scss +253 -0
  145. package/src/lib/prototype-comments/ui/comments-sidebar.module.scss +123 -0
  146. package/src/lib/prototype-comments/ui/icon-transitions.module.scss +51 -0
  147. package/src/lib/prototype-comments/ui/icons.tsx +1086 -0
  148. package/src/lib/prototype-comments/ui/is-comment-detail-path.ts +7 -0
  149. package/src/lib/prototypes/capture-prototype-viewport.ts +32 -0
  150. package/src/lib/prototypes/changelog-meta-context.tsx +162 -0
  151. package/src/lib/prototypes/changelog-meta-storage.ts +62 -0
  152. package/src/lib/prototypes/changelog-meta.ts +95 -0
  153. package/src/lib/prototypes/create-prototype-registry.ts +74 -0
  154. package/src/lib/prototypes/create-state-screenshot-thumbnail.ts +46 -0
  155. package/src/lib/prototypes/design-exploration-default-storage.ts +68 -0
  156. package/src/lib/prototypes/design-exploration-default.ts +51 -0
  157. package/src/lib/prototypes/design-exploration-types.ts +203 -0
  158. package/src/lib/prototypes/prototype-comment-registry.tsx +145 -0
  159. package/src/lib/prototypes/prototype-comment-review-bridge.tsx +40 -0
  160. package/src/lib/prototypes/prototype-comment-review-state.ts +57 -0
  161. package/src/lib/prototypes/prototype-component-registry.ts +40 -0
  162. package/src/lib/prototypes/prototype-config-types.ts +34 -0
  163. package/src/lib/prototypes/prototype-preview-state-registry.ts +443 -0
  164. package/src/lib/prototypes/prototype-preview-state-types.ts +89 -0
  165. package/src/lib/prototypes/prototype-review-context.tsx +905 -0
  166. package/src/lib/prototypes/prototype-review-url.ts +134 -0
  167. package/src/lib/prototypes/prototype-share-link.ts +175 -0
  168. package/src/lib/prototypes/prototype-state-canvas-constants.ts +118 -0
  169. package/src/lib/prototypes/prototype-state-canvas-types.ts +159 -0
  170. package/src/lib/prototypes/prototype-state-screenshot.ts +39 -0
  171. package/src/lib/prototypes/prototype-tweak-registry.ts +96 -0
  172. package/src/lib/prototypes/prototype-tweak-types.ts +31 -0
  173. package/src/lib/prototypes/reference-docs.ts +137 -0
  174. package/src/lib/prototypes/screenshot-manifest.ts +33 -0
  175. package/src/lib/prototypes/screenshot-src.ts +7 -0
  176. package/src/lib/prototypes/share-command.ts +8 -0
  177. package/src/lib/prototypes/state-map-annotation.ts +70 -0
  178. package/src/lib/prototypes/use-design-exploration-default.ts +114 -0
  179. package/src/lib/prototypes/use-persisted-local-state.ts +140 -0
  180. package/src/lib/prototypes/use-prototype-comments.ts +63 -0
  181. package/src/lib/prototypes/use-prototype-mobile-viewport-frame.ts +46 -0
  182. package/src/lib/prototypes/use-prototype-state-screenshot-src.ts +71 -0
  183. package/src/lib/prototypes/use-prototype-tool-theme.tsx +115 -0
  184. package/src/lib/prototypes/use-prototype-viewport-frame.ts +34 -0
  185. package/src/lib/prototypes/use-register-prototype-reference-docs.ts +16 -0
  186. package/src/lib/prototypes/use-snap-corner-position.ts +417 -0
  187. package/src/lib/prototypes/use-toolbar-hover-panel.ts +98 -0
  188. package/src/lib/prototypes/variant-set-lucide-icon.tsx +55 -0
  189. package/src/lib/tool-portal.ts +33 -0
  190. package/src/lib/use-copy-to-clipboard.ts +81 -0
  191. package/src/lib/utils.ts +6 -0
  192. package/src/lib/vercel-preview/parse-vercel-github-comment.ts +81 -0
  193. package/src/server/create-host-api-route.ts +100 -0
  194. package/src/server/create-prototype-api-routes.ts +428 -0
  195. package/src/server/gallery/create-gallery-api.ts +56 -0
  196. package/src/server/gallery/folders-route.ts +156 -0
  197. package/src/server/gallery/gallery-env.ts +39 -0
  198. package/src/server/gallery/gallery-route.ts +150 -0
  199. package/src/server/gallery/gallery-types.ts +15 -0
  200. package/src/server/gallery/image-route.ts +59 -0
  201. package/src/server/gallery/item-route.ts +92 -0
  202. package/src/server/pr-vercel-preview-route.ts +162 -0
  203. package/src/server/redis/client.ts +55 -0
  204. package/src/server/redis/prototype-changelog-meta.ts +31 -0
  205. package/src/server/redis/prototype-comments.ts +92 -0
  206. package/src/server/redis/prototype-design-exploration-default.ts +32 -0
  207. package/src/server.ts +51 -0
  208. package/src/styles/globals.css +624 -0
  209. package/tsconfig.json +19 -0
@@ -0,0 +1,117 @@
1
+ "use client";
2
+
3
+ import {
4
+ Dialog,
5
+ DialogClose,
6
+ DialogContent,
7
+ DialogTitle,
8
+ } from "@prototype/components/ui/dialog";
9
+ import { buildCreatePreviewStateCopyText } from "@prototype/lib/prototypes/prototype-preview-state-types";
10
+ import useCopyToClipboard from "@prototype/lib/use-copy-to-clipboard";
11
+ import { X } from "lucide-react";
12
+ import { useEffect, useState } from "react";
13
+
14
+ const MODAL_CHROME_BUTTON_CLASS =
15
+ "text-muted-foreground hover:text-foreground inline-flex size-8 shrink-0 items-center justify-center rounded-sm border-0 bg-transparent p-0 opacity-70 transition-opacity duration-200 ease hover:opacity-100 outline-none focus:outline-hidden focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-40 disabled:hover:opacity-40 disabled:hover:text-muted-foreground motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
16
+
17
+ const BRIEF_TEXTAREA_CLASS =
18
+ "field-sizing-content min-h-[min(50vh,20rem)] w-full resize-y border-0 bg-transparent px-0 py-0 text-sm leading-relaxed text-foreground shadow-none outline-none placeholder:text-muted-foreground focus-visible:ring-0";
19
+
20
+ export type PrototypeCreateStateModalProps = {
21
+ open: boolean;
22
+ onOpenChange: (open: boolean) => void;
23
+ slug: string;
24
+ existingStates: Array<{ id: string; label: string }>;
25
+ };
26
+
27
+ export function PrototypeCreateStateModal({
28
+ open,
29
+ onOpenChange,
30
+ slug,
31
+ existingStates,
32
+ }: PrototypeCreateStateModalProps) {
33
+ const [brief, setBrief] = useState("");
34
+ const { copy, icon, isCopied } = useCopyToClipboard();
35
+ const canCopy = brief.trim().length > 0;
36
+
37
+ useEffect(() => {
38
+ if (!open) {
39
+ setBrief("");
40
+ }
41
+ }, [open]);
42
+
43
+ const handleCopy = () => {
44
+ if (!canCopy) return;
45
+
46
+ const origin =
47
+ typeof window !== "undefined" ? window.location.origin : undefined;
48
+ copy(
49
+ buildCreatePreviewStateCopyText({
50
+ slug,
51
+ existingStates,
52
+ origin,
53
+ briefText: brief,
54
+ }),
55
+ );
56
+ };
57
+
58
+ return (
59
+ <Dialog open={open} onOpenChange={onOpenChange}>
60
+ <DialogContent
61
+ showCloseButton={false}
62
+ className="tool-dialog-surface flex max-h-[min(92svh,calc(100%-2rem))] w-[min(42rem,calc(100vw-2rem))] max-w-[min(42rem,calc(100vw-2rem))] flex-col gap-0 overflow-hidden border-0 p-0 sm:max-w-[min(42rem,calc(100vw-2rem))]"
63
+ >
64
+ <div className="flex items-start justify-between gap-3 px-5 py-4">
65
+ <div className="flex min-w-0 flex-col gap-1">
66
+ <DialogTitle className="text-base font-medium text-foreground">
67
+ New state
68
+ </DialogTitle>
69
+ <p className="text-sm text-muted-foreground">
70
+ Describe a page state to add — loading, empty, error, or any other
71
+ preview mode.
72
+ </p>
73
+ </div>
74
+ <DialogClose className={MODAL_CHROME_BUTTON_CLASS}>
75
+ <X />
76
+ <span className="sr-only">Close</span>
77
+ </DialogClose>
78
+ </div>
79
+
80
+ <div
81
+ className="h-px w-full shrink-0 bg-border"
82
+ role="separator"
83
+ aria-hidden
84
+ />
85
+
86
+ <div className="flex min-h-0 flex-1 flex-col overflow-y-auto px-5 py-4">
87
+ <textarea
88
+ value={brief}
89
+ onChange={(event) => setBrief(event.target.value)}
90
+ placeholder="e.g. Add an empty state when the user has no API keys yet"
91
+ aria-label="State brief"
92
+ className={BRIEF_TEXTAREA_CLASS}
93
+ />
94
+ </div>
95
+
96
+ <div
97
+ className="h-px w-full shrink-0 bg-border"
98
+ role="separator"
99
+ aria-hidden
100
+ />
101
+
102
+ <div className="flex items-center justify-end gap-2 px-5 py-4">
103
+ <button
104
+ type="button"
105
+ disabled={!canCopy}
106
+ onClick={handleCopy}
107
+ className="inline-flex h-9 items-center gap-2 rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground transition-colors duration-200 ease hover:bg-primary-dark disabled:pointer-events-none disabled:opacity-50"
108
+ aria-label="Copy state brief and prompt"
109
+ >
110
+ {isCopied ? "Copied" : "Copy prompt"}
111
+ {icon}
112
+ </button>
113
+ </div>
114
+ </DialogContent>
115
+ </Dialog>
116
+ );
117
+ }
@@ -0,0 +1,66 @@
1
+ "use client";
2
+
3
+ import { EmptyState } from "@prototype/components/platform-ui/empty-state";
4
+ import { PrototypeStateCanvasRegistrar } from "@prototype/components/prototypes/prototype-state-canvas";
5
+ import {
6
+ getDefaultPrototypeStateMapPath,
7
+ parseStateMapReturnTo,
8
+ } from "@prototype/lib/prototypes/prototype-state-canvas-types";
9
+ import Link from "next/link";
10
+ import { useSearchParams } from "next/navigation";
11
+ import { useMemo } from "react";
12
+ import { X } from "lucide-react";
13
+
14
+ import styles from "./prototype-state-canvas-overlay.module.scss";
15
+
16
+ type PrototypeDefaultStateMapPageProps = {
17
+ slug: string;
18
+ };
19
+
20
+ export function PrototypeDefaultStateMapPage({
21
+ slug,
22
+ }: PrototypeDefaultStateMapPageProps) {
23
+ const searchParams = useSearchParams();
24
+ const pagePath = getDefaultPrototypeStateMapPath(slug);
25
+ const prototypePath = `/prototypes/${slug}`;
26
+
27
+ const backHref = useMemo(() => {
28
+ const returnTo = parseStateMapReturnTo(searchParams);
29
+ if (returnTo) return returnTo;
30
+ return prototypePath;
31
+ }, [prototypePath, searchParams]);
32
+
33
+ return (
34
+ <div
35
+ className={styles.page}
36
+ data-prototype-state-canvas
37
+ data-prototype-default-state-map
38
+ >
39
+ <PrototypeStateCanvasRegistrar pagePath={pagePath} />
40
+
41
+ <div className={styles.header}>
42
+ <div>
43
+ <p className={styles.headerTitle}>State map</p>
44
+ <p className={styles.headerHint}>
45
+ Map prototype preview states once this prototype defines them.
46
+ </p>
47
+ </div>
48
+ <Link
49
+ href={backHref}
50
+ className={styles.closeButton}
51
+ aria-label="Back to prototype"
52
+ >
53
+ <X size={16} strokeWidth={2} />
54
+ </Link>
55
+ </div>
56
+
57
+ <div className="flex flex-1 items-center justify-center px-6">
58
+ <EmptyState className="max-w-md text-center">
59
+ No states mapped yet. Add a state canvas config and register this route
60
+ in `prototype.config.ts`, or follow the example-feature reference in
61
+ AGENTS.md.
62
+ </EmptyState>
63
+ </div>
64
+ </div>
65
+ );
66
+ }
@@ -0,0 +1,128 @@
1
+ "use client";
2
+
3
+ import { PrototypeComponent } from "@prototype/components/prototypes/prototype-target";
4
+ import { PrototypeDesignBriefPanel } from "@prototype/components/prototypes/prototype-design-brief-panel";
5
+ import {
6
+ Dialog,
7
+ DialogClose,
8
+ DialogContent,
9
+ DialogTitle,
10
+ } from "@prototype/components/ui/dialog";
11
+ import useCopyToClipboard from "@prototype/lib/use-copy-to-clipboard";
12
+ import { buildBriefOverviewCopyText } from "@prototype/lib/prototypes/design-exploration-types";
13
+ import type {
14
+ DesignExplorationBrief,
15
+ DesignExplorationContext,
16
+ DesignExplorationMobbin,
17
+ DesignExplorationRationale,
18
+ DesignExplorationVariantOption,
19
+ DesignExplorationVariantsSection,
20
+ } from "@prototype/lib/prototypes/design-exploration-types";
21
+ import { usePersistedLocalString } from "@prototype/lib/prototypes/use-persisted-local-state";
22
+ import type { ReactNode } from "react";
23
+ import { X } from "lucide-react";
24
+
25
+ const MODAL_CHROME_BUTTON_CLASS =
26
+ "text-muted-foreground hover:text-foreground inline-flex size-8 shrink-0 items-center justify-center rounded-sm border-0 bg-transparent p-0 opacity-70 transition-opacity hover:opacity-100 outline-none focus:outline-hidden focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-40 disabled:hover:opacity-40 disabled:hover:text-muted-foreground motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4";
27
+
28
+ type PrototypeDesignBriefModalProps<TVariant extends string> = {
29
+ open: boolean;
30
+ onOpenChange: (open: boolean) => void;
31
+ componentIdPrefix: string;
32
+ galleryId: string;
33
+ storageKeyPrefix: string;
34
+ brief: DesignExplorationBrief;
35
+ context?: DesignExplorationContext;
36
+ mobbin?: DesignExplorationMobbin;
37
+ variantsSection?: DesignExplorationVariantsSection;
38
+ variant: TVariant;
39
+ onVariantChange: (variant: TVariant) => void;
40
+ options: DesignExplorationVariantOption<TVariant>[];
41
+ rationale: Record<TVariant, DesignExplorationRationale>;
42
+ renderers: Record<TVariant, () => ReactNode>;
43
+ defaultVariantValue?: TVariant;
44
+ onSetDefaultVariant?: (value: TVariant) => void;
45
+ };
46
+
47
+ export function PrototypeDesignBriefModal<TVariant extends string>({
48
+ open,
49
+ onOpenChange,
50
+ componentIdPrefix,
51
+ galleryId,
52
+ storageKeyPrefix,
53
+ brief,
54
+ context,
55
+ mobbin,
56
+ variantsSection,
57
+ variant,
58
+ onVariantChange,
59
+ options,
60
+ rationale,
61
+ renderers,
62
+ defaultVariantValue,
63
+ onSetDefaultVariant,
64
+ }: PrototypeDesignBriefModalProps<TVariant>) {
65
+ const { copy: copyOverview, icon: copyIcon } = useCopyToClipboard();
66
+ const { value: title } = usePersistedLocalString(
67
+ `${storageKeyPrefix}-overview-title`,
68
+ brief.titleDefault,
69
+ );
70
+ const { value: description } = usePersistedLocalString(
71
+ `${storageKeyPrefix}-overview-description`,
72
+ brief.descriptionDefault,
73
+ );
74
+
75
+ const effectiveTitle = title.trim() || brief.titleDefault;
76
+ const effectiveDescription =
77
+ description.trim() || brief.descriptionDefault;
78
+
79
+ return (
80
+ <Dialog open={open} onOpenChange={onOpenChange}>
81
+ <DialogContent
82
+ className="tool-dialog-surface flex h-[min(92svh,calc(100%-2rem))] max-h-[min(92svh,calc(100%-2rem))] w-[min(96rem,calc(100vw-2rem))] max-w-[min(96rem,calc(100vw-2rem))] flex-col gap-0 overflow-hidden border-0 p-0 sm:max-w-[min(96rem,calc(100vw-2rem))]"
83
+ >
84
+ <div className="tool-dialog-accent" aria-hidden />
85
+ <div className="absolute top-4 right-4 z-10 flex items-center gap-0.5">
86
+ <PrototypeComponent id={`${componentIdPrefix}.overview-copy-brief`}>
87
+ <button
88
+ type="button"
89
+ className={MODAL_CHROME_BUTTON_CLASS}
90
+ onClick={() =>
91
+ copyOverview(
92
+ buildBriefOverviewCopyText(effectiveTitle, effectiveDescription),
93
+ )
94
+ }
95
+ aria-label="Copy overview"
96
+ >
97
+ {copyIcon}
98
+ </button>
99
+ </PrototypeComponent>
100
+ <DialogClose className={MODAL_CHROME_BUTTON_CLASS}>
101
+ <X />
102
+ <span className="sr-only">Close</span>
103
+ </DialogClose>
104
+ </div>
105
+
106
+ <DialogTitle className="sr-only">{effectiveTitle}</DialogTitle>
107
+
108
+ <PrototypeDesignBriefPanel
109
+ componentIdPrefix={componentIdPrefix}
110
+ galleryId={galleryId}
111
+ storageKeyPrefix={storageKeyPrefix}
112
+ brief={brief}
113
+ context={context}
114
+ mobbin={mobbin}
115
+ variantsSection={variantsSection}
116
+ variant={variant}
117
+ onVariantChange={onVariantChange}
118
+ options={options}
119
+ rationale={rationale}
120
+ renderers={renderers}
121
+ defaultVariantValue={defaultVariantValue}
122
+ onSetDefaultVariant={onSetDefaultVariant}
123
+ className="overflow-y-auto overscroll-contain"
124
+ />
125
+ </DialogContent>
126
+ </Dialog>
127
+ );
128
+ }