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
@@ -25,18 +25,10 @@ import {
25
25
  mergePrototypeComments,
26
26
  upsertPrototypeComment,
27
27
  } from "./redis/prototype-comments";
28
- import {
29
- isValidDesignExplorationDefaultRecord,
30
- normalizeDesignExplorationDefaultPayload,
31
- } from "@prototype/lib/prototypes/design-exploration-default";
32
28
  import {
33
29
  getPrototypeChangelogMeta,
34
30
  setPrototypeChangelogMeta,
35
31
  } from "./redis/prototype-changelog-meta";
36
- import {
37
- getPrototypeDesignExplorationDefault,
38
- setPrototypeDesignExplorationDefault,
39
- } from "./redis/prototype-design-exploration-default";
40
32
 
41
33
  type SlugRouteContext = {
42
34
  params: Promise<{ slug: string }>;
@@ -68,17 +60,6 @@ function changelogStorageUnavailableResponse() {
68
60
  );
69
61
  }
70
62
 
71
- function designDefaultStorageUnavailableResponse() {
72
- const status = getRedisStatus();
73
- return NextResponse.json(
74
- {
75
- error: "Design default storage is not configured.",
76
- missing: status.missing,
77
- },
78
- { status: 503 },
79
- );
80
- }
81
-
82
63
  export function createPrototypeCommentsRoute(config: PrototypeConfig) {
83
64
  const registry = createPrototypeRegistry(config);
84
65
 
@@ -270,102 +251,6 @@ export function createPrototypeChangelogMetaRoute(config: PrototypeConfig) {
270
251
  return { GET, PUT };
271
252
  }
272
253
 
273
- export function createPrototypeDesignExplorationDefaultRoute(
274
- config: PrototypeConfig,
275
- ) {
276
- const registry = createPrototypeRegistry(config);
277
-
278
- async function GET(request: NextRequest, { params }: SlugRouteContext) {
279
- const { slug } = await params;
280
- const prototype = registry.getPrototype(slug);
281
-
282
- if (!prototype) {
283
- return NextResponse.json({ error: "Unknown prototype" }, { status: 404 });
284
- }
285
-
286
- const storageKeyPrefix = request.nextUrl.searchParams.get("key")?.trim();
287
- if (!storageKeyPrefix) {
288
- return NextResponse.json(
289
- { error: "Missing key query parameter." },
290
- { status: 400 },
291
- );
292
- }
293
-
294
- const redisStatus = getRedisStatus();
295
- if (!redisStatus.ok) {
296
- return designDefaultStorageUnavailableResponse();
297
- }
298
-
299
- try {
300
- const record = await getPrototypeDesignExplorationDefault(storageKeyPrefix);
301
- return NextResponse.json(record);
302
- } catch (error) {
303
- console.error("Prototype design-exploration-default GET error:", error);
304
- return NextResponse.json(
305
- { error: "Failed to load design default variant" },
306
- { status: 500 },
307
- );
308
- }
309
- }
310
-
311
- async function PUT(request: NextRequest, { params }: SlugRouteContext) {
312
- const { slug } = await params;
313
- const prototype = registry.getPrototype(slug);
314
-
315
- if (!prototype) {
316
- return NextResponse.json({ error: "Unknown prototype" }, { status: 404 });
317
- }
318
-
319
- const storageKeyPrefix = request.nextUrl.searchParams.get("key")?.trim();
320
- if (!storageKeyPrefix) {
321
- return NextResponse.json(
322
- { error: "Missing key query parameter." },
323
- { status: 400 },
324
- );
325
- }
326
-
327
- const redisStatus = getRedisStatus();
328
- if (!redisStatus.ok) {
329
- return designDefaultStorageUnavailableResponse();
330
- }
331
-
332
- let body: unknown;
333
- try {
334
- body = await request.json();
335
- } catch {
336
- return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
337
- }
338
-
339
- if (!isValidDesignExplorationDefaultRecord(body)) {
340
- return NextResponse.json(
341
- { error: "Invalid design default record body" },
342
- { status: 400 },
343
- );
344
- }
345
-
346
- const record = normalizeDesignExplorationDefaultPayload(body);
347
- if (!record) {
348
- return NextResponse.json(
349
- { error: "Invalid design default record body" },
350
- { status: 400 },
351
- );
352
- }
353
-
354
- try {
355
- await setPrototypeDesignExplorationDefault(storageKeyPrefix, record);
356
- return NextResponse.json({ ok: true });
357
- } catch (error) {
358
- console.error("Prototype design-exploration-default PUT error:", error);
359
- return NextResponse.json(
360
- { error: "Failed to save design default variant" },
361
- { status: 500 },
362
- );
363
- }
364
- }
365
-
366
- return { GET, PUT };
367
- }
368
-
369
254
  export function createPrototypeScreenshotRoute(config: PrototypeConfig) {
370
255
  const registry = createPrototypeRegistry(config);
371
256
 
@@ -0,0 +1,145 @@
1
+ import { readFileSync, lstatSync } from "fs";
2
+ import { join } from "path";
3
+
4
+ import { NextResponse } from "next/server";
5
+
6
+ const PACKAGE_NAME = "proto-plugin";
7
+ const NPM_LATEST_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
8
+ const CACHE_TTL_MS = 60 * 60 * 1000;
9
+
10
+ export type ProtoPluginVersionStatus = {
11
+ installed: string | null;
12
+ latest: string | null;
13
+ updateAvailable: boolean;
14
+ isWorkspaceLink: boolean;
15
+ repositoryUrl?: string;
16
+ };
17
+
18
+ type NpmLatestResponse = {
19
+ version?: string;
20
+ repository?: { url?: string };
21
+ };
22
+
23
+ let cachedLatest: { version: string; repositoryUrl?: string; fetchedAt: number } | null =
24
+ null;
25
+
26
+ function parseSemver(version: string): [number, number, number] | null {
27
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
28
+ if (!match) return null;
29
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
30
+ }
31
+
32
+ export function isNewerVersion(latest: string, installed: string): boolean {
33
+ const latestParts = parseSemver(latest);
34
+ const installedParts = parseSemver(installed);
35
+ if (!latestParts || !installedParts) return false;
36
+
37
+ for (let index = 0; index < 3; index += 1) {
38
+ if (latestParts[index]! > installedParts[index]!) return true;
39
+ if (latestParts[index]! < installedParts[index]!) return false;
40
+ }
41
+
42
+ return false;
43
+ }
44
+
45
+ function readInstalledVersion(cwd: string): {
46
+ installed: string | null;
47
+ isWorkspaceLink: boolean;
48
+ } {
49
+ const packagePath = join(cwd, "node_modules", PACKAGE_NAME, "package.json");
50
+ const linkPath = join(cwd, "node_modules", PACKAGE_NAME);
51
+
52
+ let isWorkspaceLink = false;
53
+
54
+ try {
55
+ isWorkspaceLink = lstatSync(linkPath).isSymbolicLink();
56
+ } catch {
57
+ try {
58
+ const hostPkg = JSON.parse(
59
+ readFileSync(join(cwd, "package.json"), "utf8"),
60
+ ) as {
61
+ dependencies?: Record<string, string>;
62
+ devDependencies?: Record<string, string>;
63
+ };
64
+ const depSpec =
65
+ hostPkg.dependencies?.[PACKAGE_NAME] ??
66
+ hostPkg.devDependencies?.[PACKAGE_NAME];
67
+ isWorkspaceLink = depSpec?.startsWith("workspace:") ?? false;
68
+ } catch {
69
+ // Ignore — treat as non-workspace.
70
+ }
71
+ }
72
+
73
+ try {
74
+ const pkg = JSON.parse(readFileSync(packagePath, "utf8")) as { version?: string };
75
+ return { installed: pkg.version ?? null, isWorkspaceLink };
76
+ } catch {
77
+ return { installed: null, isWorkspaceLink };
78
+ }
79
+ }
80
+
81
+ async function fetchLatestFromNpm(): Promise<{
82
+ version: string | null;
83
+ repositoryUrl?: string;
84
+ }> {
85
+ const now = Date.now();
86
+
87
+ if (cachedLatest && now - cachedLatest.fetchedAt < CACHE_TTL_MS) {
88
+ return {
89
+ version: cachedLatest.version,
90
+ repositoryUrl: cachedLatest.repositoryUrl,
91
+ };
92
+ }
93
+
94
+ try {
95
+ const response = await fetch(NPM_LATEST_URL, {
96
+ next: { revalidate: 3600 },
97
+ });
98
+
99
+ if (!response.ok) {
100
+ return { version: null };
101
+ }
102
+
103
+ const data = (await response.json()) as NpmLatestResponse;
104
+ const version = data.version ?? null;
105
+ const repositoryUrl = data.repository?.url?.replace(/^git\+/, "").replace(/\.git$/, "");
106
+
107
+ if (version) {
108
+ cachedLatest = { version, repositoryUrl, fetchedAt: now };
109
+ }
110
+
111
+ return { version, repositoryUrl };
112
+ } catch {
113
+ return { version: null };
114
+ }
115
+ }
116
+
117
+ export async function GET() {
118
+ const cwd = process.cwd();
119
+ const { installed, isWorkspaceLink } = readInstalledVersion(cwd);
120
+
121
+ if (isWorkspaceLink) {
122
+ const status: ProtoPluginVersionStatus = {
123
+ installed,
124
+ latest: null,
125
+ updateAvailable: false,
126
+ isWorkspaceLink: true,
127
+ };
128
+ return NextResponse.json(status);
129
+ }
130
+
131
+ const { version: latest, repositoryUrl } = await fetchLatestFromNpm();
132
+
133
+ const updateAvailable =
134
+ installed != null && latest != null && isNewerVersion(latest, installed);
135
+
136
+ const status: ProtoPluginVersionStatus = {
137
+ installed,
138
+ latest,
139
+ updateAvailable,
140
+ isWorkspaceLink: false,
141
+ repositoryUrl,
142
+ };
143
+
144
+ return NextResponse.json(status);
145
+ }
@@ -0,0 +1,29 @@
1
+ import { NextResponse } from "next/server";
2
+
3
+ import {
4
+ getGalleryKvStatus,
5
+ getGalleryStorageStatus,
6
+ normalizeGalleryEnv,
7
+ } from "./gallery/gallery-env";
8
+ import { getRedisStatus, normalizeRedisEnv } from "./redis/client";
9
+
10
+ export function GET() {
11
+ normalizeRedisEnv();
12
+ normalizeGalleryEnv();
13
+
14
+ const comments = getRedisStatus();
15
+ const galleryKv = getGalleryKvStatus();
16
+ const gallery = getGalleryStorageStatus();
17
+
18
+ return NextResponse.json({
19
+ comments: {
20
+ configured: comments.ok,
21
+ missing: comments.missing,
22
+ },
23
+ gallery: {
24
+ kvConfigured: galleryKv.ok,
25
+ storageConfigured: gallery.ok,
26
+ missing: gallery.missing,
27
+ },
28
+ });
29
+ }
package/src/server.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import "server-only";
2
2
 
3
+ export { createPrototypeGalleryPage } from "./config/create-prototype-gallery-page";
4
+
3
5
  export {
4
6
  readScreenshotManifest,
5
7
  touchScreenshotManifest,
@@ -12,7 +14,6 @@ export {
12
14
  createPrototypeChangelogMetaRoute,
13
15
  createPrototypeCommentIdRoute,
14
16
  createPrototypeCommentsRoute,
15
- createPrototypeDesignExplorationDefaultRoute,
16
17
  createPrototypeScreenshotRoute,
17
18
  } from "./server/create-prototype-api-routes";
18
19
 
@@ -42,10 +43,3 @@ export {
42
43
  getPrototypeChangelogMeta,
43
44
  setPrototypeChangelogMeta,
44
45
  } from "./server/redis/prototype-changelog-meta";
45
-
46
- export {
47
- getPrototypeDesignExplorationDefault,
48
- setPrototypeDesignExplorationDefault,
49
- } from "./server/redis/prototype-design-exploration-default";
50
-
51
- export { prototypeDesignExplorationDefaultRedisKey } from "./lib/prototypes/design-exploration-default";