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
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "proto-plugin",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "publishConfig": {
6
+ "registry": "https://registry.npmjs.org"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/maayanalbert/proto-plugin.git",
11
+ "directory": "packages/prototype"
12
+ },
13
+ "files": [
14
+ "src",
15
+ "scripts",
16
+ "README.md",
17
+ "AGENTS.md",
18
+ "components.json",
19
+ "tsconfig.json"
20
+ ],
21
+ "main": "./src/index.ts",
22
+ "types": "./src/index.ts",
23
+ "exports": {
24
+ ".": "./src/index.ts",
25
+ "./config": "./src/config/define-prototype-config.ts",
26
+ "./config/prototype-icon": "./src/config/prototype-icon.tsx",
27
+ "./app": "./src/app.ts",
28
+ "./with-prototype": "./src/config/with-prototype.ts",
29
+ "./server": "./src/server.ts",
30
+ "./styles/globals.css": "./src/styles/globals.css"
31
+ },
32
+ "scripts": {
33
+ "verify:prototype-ids": "node scripts/verify-prototype-component-ids.mjs",
34
+ "verify:prototype-preview-states": "node scripts/verify-prototype-preview-states.mjs",
35
+ "link-source": "./scripts/link-source.sh",
36
+ "sync-from-source": "./scripts/sync-from-source.sh",
37
+ "link-source-db": "./scripts/link-source-db.sh",
38
+ "share-prototype": "./scripts/share-prototype.sh",
39
+ "download-mobbin-references": "node scripts/download-mobbin-reference-images.mjs",
40
+ "publish-package": "./scripts/publish-package.sh"
41
+ },
42
+ "peerDependencies": {
43
+ "next": ">=15",
44
+ "react": ">=19",
45
+ "react-dom": ">=19"
46
+ },
47
+ "dependencies": {
48
+ "@radix-ui/react-dialog": "^1.1.15",
49
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
50
+ "@radix-ui/react-label": "^2.1.8",
51
+ "@radix-ui/react-scroll-area": "^1.2.10",
52
+ "@radix-ui/react-select": "^2.2.6",
53
+ "@radix-ui/react-separator": "^1.1.8",
54
+ "@radix-ui/react-slider": "^1.3.6",
55
+ "@radix-ui/react-slot": "^1.2.4",
56
+ "@radix-ui/react-switch": "^1.2.6",
57
+ "@radix-ui/react-tabs": "^1.1.13",
58
+ "@radix-ui/react-toggle-group": "^1.1.11",
59
+ "@radix-ui/react-tooltip": "^1.2.8",
60
+ "@upstash/redis": "^1.38.0",
61
+ "@vercel/blob": "^2.0.0",
62
+ "@vercel/kv": "^2.0.0",
63
+ "class-variance-authority": "^0.7.1",
64
+ "clsx": "^2.1.0",
65
+ "html-to-image": "^1.11.13",
66
+ "lucide-react": "^0.543.0",
67
+ "modern-screenshot": "^4.6.0",
68
+ "sass": "^1.86.0",
69
+ "server-only": "^0.0.1",
70
+ "sonner": "^1.7.4",
71
+ "tailwind-merge": "^2.5.2"
72
+ }
73
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Fetches full-resolution (1920w) Mobbin screen images for prototype references.
3
+ * MCP short URLs are ~768px — too small for the overview modal grid.
4
+ *
5
+ * Usage:
6
+ * pnpm download-mobbin-references -- --ids=id1,id2,id3
7
+ * pnpm download-mobbin-references -- --config=path/to/ids.json
8
+ *
9
+ * Config JSON shape: { "screenIds": ["uuid", ...] }
10
+ */
11
+
12
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
13
+ import path from "node:path";
14
+ import {
15
+ resolveHostRoot,
16
+ resolveToolRoot,
17
+ } from "./lib/resolve-host-root.mjs";
18
+
19
+ const toolRoot = resolveToolRoot(import.meta.url);
20
+ const hostRoot = resolveHostRoot(toolRoot);
21
+ const OUT_DIR = path.join(hostRoot, "public/prototypes/mobbin-references");
22
+
23
+ function parseArgs(argv) {
24
+ const idsFlag = argv.find((arg) => arg.startsWith("--ids="));
25
+ if (idsFlag) {
26
+ const ids = idsFlag
27
+ .slice("--ids=".length)
28
+ .split(",")
29
+ .map((id) => id.trim())
30
+ .filter(Boolean);
31
+ if (ids.length === 0) {
32
+ throw new Error("--ids requires at least one screen id");
33
+ }
34
+ return ids;
35
+ }
36
+
37
+ const configFlag = argv.find((arg) => arg.startsWith("--config="));
38
+ if (configFlag) {
39
+ return { configPath: configFlag.slice("--config=".length) };
40
+ }
41
+
42
+ throw new Error(
43
+ "No screen ids provided. Pass --ids=id1,id2 or --config=path/to/ids.json",
44
+ );
45
+ }
46
+
47
+ async function loadScreenIds(argv) {
48
+ const parsed = parseArgs(argv);
49
+ if (Array.isArray(parsed)) return parsed;
50
+
51
+ const raw = await readFile(parsed.configPath, "utf8");
52
+ const config = JSON.parse(raw);
53
+ const ids = config.screenIds ?? config.ids;
54
+ if (!Array.isArray(ids) || ids.length === 0) {
55
+ throw new Error("Config must include a non-empty screenIds (or ids) array");
56
+ }
57
+ return ids;
58
+ }
59
+
60
+ function extractLargestSrcSetUrl(html) {
61
+ const srcSetMatch = html.match(/imageSrcSet="([^"]+)"/);
62
+ if (!srcSetMatch) return null;
63
+
64
+ const candidates = [];
65
+ const parts = srcSetMatch[1].split(/\s+/);
66
+ for (let i = 0; i < parts.length - 1; i += 2) {
67
+ const url = parts[i];
68
+ const width = parts[i + 1];
69
+ if (url?.startsWith("https://") && width?.endsWith("w")) {
70
+ candidates.push({ url, width: Number.parseInt(width, 10) });
71
+ }
72
+ }
73
+
74
+ candidates.sort((a, b) => b.width - a.width);
75
+ return candidates[0]?.url ?? null;
76
+ }
77
+
78
+ async function downloadScreen(screenId) {
79
+ const pageUrl = `https://mobbin.com/explore/screens/${screenId}`;
80
+ const response = await fetch(pageUrl, {
81
+ headers: { "User-Agent": "prototype-tool/1.0" },
82
+ });
83
+ if (!response.ok) {
84
+ throw new Error(`${screenId}: page fetch failed (${response.status})`);
85
+ }
86
+
87
+ const html = await response.text();
88
+ const imageUrl = extractLargestSrcSetUrl(html);
89
+ if (!imageUrl) {
90
+ throw new Error(`${screenId}: no srcset image found`);
91
+ }
92
+
93
+ const imageResponse = await fetch(imageUrl);
94
+ if (!imageResponse.ok) {
95
+ throw new Error(`${screenId}: image fetch failed (${imageResponse.status})`);
96
+ }
97
+
98
+ const buffer = Buffer.from(await imageResponse.arrayBuffer());
99
+ const outPath = path.join(OUT_DIR, `${screenId}.webp`);
100
+ await writeFile(outPath, buffer);
101
+ console.log(`Saved ${outPath} (${buffer.length} bytes)`);
102
+ }
103
+
104
+ const screenIds = await loadScreenIds(process.argv.slice(2));
105
+
106
+ await mkdir(OUT_DIR, { recursive: true });
107
+
108
+ for (const screenId of screenIds) {
109
+ await downloadScreen(screenId);
110
+ }
111
+
112
+ console.log("Done.");
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Host-owned sync/link config (TEMPLATE).
4
+ #
5
+ # Copy this to the host repo root as `prototype.sync.config.sh` and fill in
6
+ # the values for YOUR app. The prototype package reads this file so that no
7
+ # host-specific file names, component customizations, or source-app env var
8
+ # names ever live inside the package itself.
9
+ #
10
+ # All paths are relative to the repo root and are mirrored 1:1 between the
11
+ # source app and this host (e.g. source `src/lib/utils.ts` -> host
12
+ # `src/lib/utils.ts`).
13
+
14
+ # --- pnpm sync-from-source -------------------------------------------------
15
+
16
+ # Single files to copy from the source app into this host.
17
+ SYNC_FILES=(
18
+ "src/app/globals.css"
19
+ # "src/lib/utils.ts"
20
+ )
21
+
22
+ # Directories to copy recursively (contents merged into the host dir).
23
+ SYNC_DIRS=(
24
+ # "src/components/ui"
25
+ )
26
+
27
+ # Globs to copy (each glob's parent dir is the destination).
28
+ SYNC_GLOBS=(
29
+ # "public/fonts/*.woff2"
30
+ )
31
+
32
+ # Reminder list printed after a sync: files this host has customized beyond
33
+ # the source version, which the blanket copy overwrites and you must re-apply.
34
+ # Leave empty if the host does not diverge from source.
35
+ SYNC_LOCAL_EXTENSIONS=(
36
+ # "my-component (local variant + semantic token classes)"
37
+ )
38
+
39
+ # --- pnpm link-source-db ---------------------------------------------------
40
+
41
+ # Env var names to copy from the source app's .env.local into this host's.
42
+ SOURCE_DB_ENV_VARS=(
43
+ # "MY_SERVICE_URL"
44
+ # "MY_SERVICE_KEY"
45
+ )
46
+
47
+ # Optional: console URL printed after a successful link.
48
+ SOURCE_DB_CONSOLE_URL=""
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Locate and source the host-owned sync/link config.
4
+ #
5
+ # The prototype package is host-agnostic: it must not name host files,
6
+ # host component customizations, or source-app env vars. All of that lives
7
+ # in a host-root config file owned by the consuming app.
8
+ #
9
+ # Resolution: PROTOTYPE_SYNC_CONFIG (env) > $HOST_ROOT/prototype.sync.config.sh
10
+ #
11
+ # See scripts/lib/host-config.example.sh for the expected shape.
12
+ load_host_config() {
13
+ local host_root="$1"
14
+ local config="${PROTOTYPE_SYNC_CONFIG:-$host_root/prototype.sync.config.sh}"
15
+
16
+ if [[ ! -f "$config" ]]; then
17
+ echo "Host sync config not found at: $config" >&2
18
+ echo "Create it in the host repo root. Template:" >&2
19
+ echo " packages/prototype/scripts/lib/host-config.example.sh" >&2
20
+ return 1
21
+ fi
22
+
23
+ # shellcheck disable=SC1090
24
+ source "$config"
25
+ }
@@ -0,0 +1,18 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export function resolveHostRoot(toolRootPath) {
6
+ let dir = path.resolve(toolRootPath, "..");
7
+ while (dir !== path.dirname(dir)) {
8
+ if (fs.existsSync(path.join(dir, "prototype.config.ts"))) {
9
+ return dir;
10
+ }
11
+ dir = path.dirname(dir);
12
+ }
13
+ return path.resolve(toolRootPath, "..", "..");
14
+ }
15
+
16
+ export function resolveToolRoot(importMetaUrl) {
17
+ return path.resolve(path.dirname(fileURLToPath(importMetaUrl)), "..");
18
+ }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Walk up from the plugin root until we find prototype.config.ts (host repo root).
4
+ resolve_host_root() {
5
+ local tool_root="$1"
6
+ local dir
7
+
8
+ dir="$(cd "$tool_root/.." && pwd)"
9
+ while [[ "$dir" != "/" ]]; do
10
+ if [[ -f "$dir/prototype.config.ts" ]]; then
11
+ printf '%s' "$dir"
12
+ return 0
13
+ fi
14
+ dir="$(dirname "$dir")"
15
+ done
16
+
17
+ # Fallback for the standard workspace layout: host/packages/prototype
18
+ (cd "$tool_root/../.." && pwd)
19
+ }
20
+
21
+ # Resolve source app path: SOURCE (shell) > SOURCE_PATH (env) > .env.local > source/ symlink
22
+ resolve_source_path() {
23
+ local root="$1"
24
+ local raw="${SOURCE:-${SOURCE_PATH:-}}"
25
+
26
+ if [[ -z "$raw" && -f "$root/.env.local" ]]; then
27
+ raw="$(
28
+ grep -E '^SOURCE_PATH=' "$root/.env.local" | tail -1 | cut -d= -f2- \
29
+ | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' \
30
+ -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//"
31
+ )"
32
+ fi
33
+
34
+ if [[ -z "$raw" && -L "$root/source" ]]; then
35
+ raw="$(readlink "$root/source")"
36
+ fi
37
+
38
+ if [[ -z "$raw" ]]; then
39
+ return 1
40
+ fi
41
+
42
+ raw="${raw/#\~/$HOME}"
43
+
44
+ if [[ "$raw" != /* ]]; then
45
+ raw="$root/$raw"
46
+ fi
47
+
48
+ (cd "$raw" 2>/dev/null && pwd) || return 1
49
+ }
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ TOOL_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+ # shellcheck source=lib/source-path.sh
6
+ source "$TOOL_ROOT/scripts/lib/source-path.sh"
7
+ # shellcheck source=lib/host-config.sh
8
+ source "$TOOL_ROOT/scripts/lib/host-config.sh"
9
+ HOST_ROOT="$(resolve_host_root "$TOOL_ROOT")"
10
+
11
+ if ! load_host_config "$HOST_ROOT"; then
12
+ exit 1
13
+ fi
14
+
15
+ if [[ "${SOURCE_DB_ENV_VARS+set}" != set || "${#SOURCE_DB_ENV_VARS[@]}" -eq 0 ]]; then
16
+ echo "No SOURCE_DB_ENV_VARS configured in the host sync config." >&2
17
+ echo "Add the env var names to copy from the source app to prototype.sync.config.sh." >&2
18
+ exit 1
19
+ fi
20
+
21
+ TARGET="${1:-}"
22
+ if [[ -z "$TARGET" ]]; then
23
+ if ! TARGET="$(resolve_source_path "$HOST_ROOT")"; then
24
+ echo "Set SOURCE_PATH in .env.local (see .env.example), or pass a path:" >&2
25
+ echo " pnpm link-source-db /path/to/source-app" >&2
26
+ exit 1
27
+ fi
28
+ elif [[ "$TARGET" != /* ]]; then
29
+ TARGET="$HOST_ROOT/$TARGET"
30
+ fi
31
+
32
+ SOURCE_ENV="$TARGET/.env.local"
33
+ LOCAL_ENV="$HOST_ROOT/.env.local"
34
+
35
+ if [[ ! -d "$TARGET" ]]; then
36
+ echo "Source app not found at: $TARGET" >&2
37
+ exit 1
38
+ fi
39
+
40
+ if [[ ! -f "$SOURCE_ENV" ]]; then
41
+ echo "Source env file not found at: $SOURCE_ENV" >&2
42
+ echo "Create it in the source app repo (see source app local dev docs)." >&2
43
+ exit 1
44
+ fi
45
+
46
+ extract_env() {
47
+ local file="$1"
48
+ local key="$2"
49
+ local line value
50
+
51
+ line="$(grep -E "^(#\s*)?${key}=" "$file" | tail -1 || true)"
52
+ if [[ -z "$line" ]]; then
53
+ return 1
54
+ fi
55
+
56
+ value="${line#"${line%%=*}"=}"
57
+ value="${value#"${value%%[![:space:]]*}"}"
58
+ value="${value%"${value##*[![:space:]]}"}"
59
+ value="${value#"${value%%[![:space:]]*}"}"
60
+ value="${value%"${value##*[![:space:]]}"}"
61
+
62
+ if [[ "$value" == \"*\" && "$value" == *\" ]]; then
63
+ value="${value:1:${#value}-2}"
64
+ elif [[ "$value" == \'*\' && "$value" == *\' ]]; then
65
+ value="${value:1:${#value}-2}"
66
+ fi
67
+
68
+ printf '%s' "$value"
69
+ }
70
+
71
+ upsert_env() {
72
+ local file="$1"
73
+ local key="$2"
74
+ local value="$3"
75
+ local tmp escaped
76
+
77
+ escaped="${value//\\/\\\\}"
78
+ escaped="${escaped//\"/\\\"}"
79
+
80
+ if [[ ! -f "$file" ]]; then
81
+ printf '%s="%s"\n' "$key" "$escaped" >>"$file"
82
+ return
83
+ fi
84
+
85
+ tmp="$(mktemp)"
86
+ if grep -q "^${key}=" "$file"; then
87
+ awk -v key="$key" -v value="$escaped" '
88
+ BEGIN { updated = 0 }
89
+ $0 ~ "^" key "=" {
90
+ print key "=\"" value "\""
91
+ updated = 1
92
+ next
93
+ }
94
+ { print }
95
+ END {
96
+ if (!updated) {
97
+ print key "=\"" value "\""
98
+ }
99
+ }
100
+ ' "$file" >"$tmp"
101
+ else
102
+ cp "$file" "$tmp"
103
+ printf '\n%s="%s"\n' "$key" "$escaped" >>"$tmp"
104
+ fi
105
+
106
+ mv "$tmp" "$file"
107
+ }
108
+
109
+ linked=0
110
+ for key in "${SOURCE_DB_ENV_VARS[@]}"; do
111
+ if value="$(extract_env "$SOURCE_ENV" "$key")"; then
112
+ upsert_env "$LOCAL_ENV" "$key" "$value"
113
+ echo "Linked $key from $SOURCE_ENV"
114
+ linked=$((linked + 1))
115
+ fi
116
+ done
117
+
118
+ if [[ "$linked" -eq 0 ]]; then
119
+ echo "No configured vars found in $SOURCE_ENV." >&2
120
+ echo "Uncomment or add the SOURCE_DB_ENV_VARS entries in the source app's .env.local first." >&2
121
+ exit 1
122
+ fi
123
+
124
+ if [[ -n "${SOURCE_DB_CONSOLE_URL:-}" ]]; then
125
+ echo "Updated $LOCAL_ENV ($linked vars). Console: $SOURCE_DB_CONSOLE_URL"
126
+ else
127
+ echo "Updated $LOCAL_ENV ($linked vars)."
128
+ fi
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ TOOL_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+ # shellcheck source=lib/source-path.sh
6
+ source "$TOOL_ROOT/scripts/lib/source-path.sh"
7
+ HOST_ROOT="$(resolve_host_root "$TOOL_ROOT")"
8
+
9
+ LINK="$HOST_ROOT/source"
10
+ TARGET="${1:-}"
11
+
12
+ if [[ -z "$TARGET" ]]; then
13
+ if ! TARGET="$(resolve_source_path "$HOST_ROOT")"; then
14
+ echo "Set SOURCE_PATH in .env.local (see .env.example), or pass a path:" >&2
15
+ echo " pnpm link-source /path/to/source-app" >&2
16
+ exit 1
17
+ fi
18
+ elif [[ "$TARGET" != /* ]]; then
19
+ TARGET="$HOST_ROOT/$TARGET"
20
+ fi
21
+
22
+ if [[ ! -d "$TARGET" ]]; then
23
+ echo "Source app not found at: $TARGET" >&2
24
+ exit 1
25
+ fi
26
+
27
+ if [[ -L "$LINK" ]]; then
28
+ current="$(readlink "$LINK")"
29
+ if [[ "$current" == "$TARGET" ]] || [[ "$(cd "$HOST_ROOT" && cd "$current" 2>/dev/null && pwd)" == "$TARGET" ]]; then
30
+ echo "source/ already linked to: $TARGET"
31
+ exit 0
32
+ fi
33
+ rm "$LINK"
34
+ elif [[ -e "$LINK" ]]; then
35
+ echo "source/ exists but is not a symlink — remove it first" >&2
36
+ exit 1
37
+ fi
38
+
39
+ ln -s "$TARGET" "$LINK"
40
+ echo "Linked source/ -> $TARGET"
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ TOOL_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
5
+ cd "$TOOL_ROOT"
6
+
7
+ REGISTRY="https://registry.npmjs.org"
8
+ DRY_RUN=0
9
+ TAG="latest"
10
+ SKIP_VERIFY=0
11
+ OTP=""
12
+
13
+ usage() {
14
+ cat <<EOF
15
+ Usage: $(basename "$0") [options]
16
+
17
+ Publish proto-plugin to npm.
18
+
19
+ Options:
20
+ --dry-run Validate the tarball without publishing
21
+ --tag <tag> npm dist-tag (default: latest)
22
+ --otp <code> One-time password from your authenticator app
23
+ --skip-verify Skip pre-publish verification scripts
24
+ -h, --help Show this help message
25
+
26
+ Examples:
27
+ pnpm publish-package
28
+ pnpm publish-package --otp 123456
29
+ pnpm publish-package --dry-run
30
+ pnpm publish-package --tag next
31
+
32
+ Requires: npm login (npm whoami must succeed).
33
+ EOF
34
+ }
35
+
36
+ while [[ $# -gt 0 ]]; do
37
+ case "$1" in
38
+ --dry-run)
39
+ DRY_RUN=1
40
+ shift
41
+ ;;
42
+ --tag)
43
+ TAG="${2:?Missing value for --tag}"
44
+ shift 2
45
+ ;;
46
+ --skip-verify)
47
+ SKIP_VERIFY=1
48
+ shift
49
+ ;;
50
+ --otp)
51
+ OTP="${2:?Missing value for --otp}"
52
+ shift 2
53
+ ;;
54
+ -h | --help)
55
+ usage
56
+ exit 0
57
+ ;;
58
+ *)
59
+ echo "Unknown option: $1" >&2
60
+ usage >&2
61
+ exit 1
62
+ ;;
63
+ esac
64
+ done
65
+
66
+ if [[ "$(node -pe "require('./package.json').private === true")" == "true" ]]; then
67
+ echo "package.json has \"private\": true, which blocks npm publish." >&2
68
+ echo "Remove it or set \"private\": false before publishing." >&2
69
+ exit 1
70
+ fi
71
+
72
+ if ! npm whoami --registry "$REGISTRY" >/dev/null 2>&1; then
73
+ echo "Not logged in to npm. Run: npm login" >&2
74
+ exit 1
75
+ fi
76
+
77
+ find_host_with_prototypes() {
78
+ local dir="$TOOL_ROOT/.."
79
+ while [[ "$dir" != "/" ]]; do
80
+ if [[ -f "$dir/prototype.config.ts" && -d "$dir/src/prototypes" ]]; then
81
+ echo "$dir"
82
+ return 0
83
+ fi
84
+ dir="$(dirname "$dir")"
85
+ done
86
+ return 1
87
+ }
88
+
89
+ if [[ "$SKIP_VERIFY" -eq 0 ]]; then
90
+ if host_root="$(find_host_with_prototypes)"; then
91
+ echo "Running verification scripts against $host_root..."
92
+ npm run verify:prototype-ids
93
+ npm run verify:prototype-preview-states
94
+ else
95
+ echo "Skipping verification (no host app with prototypes found)."
96
+ fi
97
+ fi
98
+
99
+ PACKAGE_NAME="$(node -pe "require('./package.json').name")"
100
+ PACKAGE_VERSION="$(node -pe "require('./package.json').version")"
101
+
102
+ echo "Publishing ${PACKAGE_NAME}@${PACKAGE_VERSION}"
103
+ echo "Registry: ${REGISTRY}"
104
+ echo "Access: public (unscoped)"
105
+
106
+ PUBLISH_ARGS=(--registry "$REGISTRY" --tag "$TAG")
107
+
108
+ if [[ -n "$OTP" ]]; then
109
+ PUBLISH_ARGS+=(--otp "$OTP")
110
+ fi
111
+
112
+ if [[ "$DRY_RUN" -eq 1 ]]; then
113
+ PUBLISH_ARGS+=(--dry-run)
114
+ echo "Dry run — tarball will not be uploaded."
115
+ fi
116
+
117
+ env -u NPM_CONFIG_REGISTRY -u NPM_CONFIG_CAFILE \
118
+ npm publish "${PUBLISH_ARGS[@]}" || {
119
+ status=$?
120
+ if [[ "$DRY_RUN" -eq 0 && "$status" -ne 0 ]]; then
121
+ echo >&2
122
+ echo "Publish failed." >&2
123
+ if [[ "$PACKAGE_NAME" == @*/* ]]; then
124
+ scope="${PACKAGE_NAME%%/*}"
125
+ scope="${scope#@}"
126
+ whoami="$(npm whoami --registry "$REGISTRY" 2>/dev/null || true)"
127
+ echo "Logged in as: ${whoami:-unknown}" >&2
128
+ echo "Package scope: @${scope}" >&2
129
+ if [[ -n "$whoami" && "$whoami" != "$scope" ]]; then
130
+ echo >&2
131
+ echo "npm often returns 404 when your account cannot publish to this scope." >&2
132
+ echo "Fix one of:" >&2
133
+ echo " 1. npm logout && npm login # sign in as the @${scope} npm user/org owner" >&2
134
+ echo " 2. Add @${whoami} to the @${scope} org at https://www.npmjs.com/settings/${scope}/members" >&2
135
+ echo " 3. Rename the package to @${whoami}/prototype in package.json" >&2
136
+ fi
137
+ fi
138
+ fi
139
+ exit "$status"
140
+ }
141
+
142
+ if [[ "$DRY_RUN" -eq 0 ]]; then
143
+ echo "Published successfully."
144
+ echo "Install with:"
145
+ echo " npm install ${PACKAGE_NAME}@${PACKAGE_VERSION}"
146
+ echo " pnpm add ${PACKAGE_NAME}@${PACKAGE_VERSION}"
147
+ fi