proto-plugin 0.1.1 → 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 +48 -6
- package/package.json +1 -1
- package/scripts/lib/host-config.example.sh +1 -1
- 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
|
@@ -28,7 +28,7 @@ type PrototypeToolThemeContextValue = {
|
|
|
28
28
|
const PrototypeToolThemeContext =
|
|
29
29
|
createContext<PrototypeToolThemeContextValue | null>(null);
|
|
30
30
|
|
|
31
|
-
const DEFAULT_THEME: PrototypeToolTheme = "
|
|
31
|
+
const DEFAULT_THEME: PrototypeToolTheme = "light";
|
|
32
32
|
|
|
33
33
|
function normalizeTheme(value: string): PrototypeToolTheme {
|
|
34
34
|
return value === "light" ? "light" : "dark";
|
|
@@ -8,12 +8,21 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
export const TOOLBAR_PANEL_HOVER_OPEN_DELAY_MS = 100;
|
|
10
10
|
export const TOOLBAR_PANEL_HOVER_CLOSE_DELAY_MS = 150;
|
|
11
|
+
export const TOOLBAR_HOVER_CAPABLE_MEDIA_QUERY =
|
|
12
|
+
"(hover: hover) and (pointer: fine)";
|
|
13
|
+
|
|
14
|
+
export function supportsToolbarHover() {
|
|
15
|
+
if (typeof window === "undefined") return true;
|
|
16
|
+
return window.matchMedia(TOOLBAR_HOVER_CAPABLE_MEDIA_QUERY).matches;
|
|
17
|
+
}
|
|
11
18
|
|
|
12
19
|
export function preventToolbarHoverButtonActivation(
|
|
13
20
|
event:
|
|
14
21
|
| ReactMouseEvent<HTMLButtonElement>
|
|
15
22
|
| ReactPointerEvent<HTMLButtonElement>,
|
|
16
23
|
) {
|
|
24
|
+
if (!supportsToolbarHover()) return;
|
|
25
|
+
|
|
17
26
|
event.preventDefault();
|
|
18
27
|
event.stopPropagation();
|
|
19
28
|
}
|
|
@@ -59,7 +68,7 @@ export function useToolbarHoverPanel({
|
|
|
59
68
|
|
|
60
69
|
const scheduleOpen = useCallback(
|
|
61
70
|
(event: ReactPointerEvent<HTMLElement>) => {
|
|
62
|
-
if (event.pointerType === "touch") return;
|
|
71
|
+
if (!supportsToolbarHover() || event.pointerType === "touch") return;
|
|
63
72
|
|
|
64
73
|
clearCloseTimer();
|
|
65
74
|
clearOpenTimer();
|
|
@@ -71,6 +80,8 @@ export function useToolbarHoverPanel({
|
|
|
71
80
|
);
|
|
72
81
|
|
|
73
82
|
const scheduleClose = useCallback(() => {
|
|
83
|
+
if (!supportsToolbarHover()) return;
|
|
84
|
+
|
|
74
85
|
clearOpenTimer();
|
|
75
86
|
clearCloseTimer();
|
|
76
87
|
closeTimerRef.current = setTimeout(() => {
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import type { LucideIcon, LucideProps } from "lucide-react";
|
|
2
2
|
import {
|
|
3
3
|
ArrowDownUp,
|
|
4
|
+
BadgeCheck,
|
|
4
5
|
CalendarDays,
|
|
5
6
|
Clock,
|
|
6
7
|
FilterX,
|
|
8
|
+
Hexagon,
|
|
7
9
|
Inbox,
|
|
8
10
|
LayoutGrid,
|
|
11
|
+
LayoutList,
|
|
12
|
+
LayoutTemplate,
|
|
13
|
+
PanelsTopLeft,
|
|
14
|
+
MessageSquareText,
|
|
15
|
+
Palette,
|
|
9
16
|
PanelBottom,
|
|
17
|
+
PanelBottomOpen,
|
|
18
|
+
Send,
|
|
10
19
|
Shapes,
|
|
11
20
|
Smartphone,
|
|
21
|
+
Sparkles,
|
|
12
22
|
Table,
|
|
13
23
|
} from "lucide-react";
|
|
14
24
|
|
|
@@ -19,6 +29,20 @@ type VariantSetIconRule = {
|
|
|
19
29
|
|
|
20
30
|
/** First match wins — order rules from most specific to broadest. */
|
|
21
31
|
const VARIANT_SET_ICON_RULES: VariantSetIconRule[] = [
|
|
32
|
+
{ test: /event-types-page-layout|event types page layout/i, icon: PanelsTopLeft },
|
|
33
|
+
// proto-partner-page explorations
|
|
34
|
+
{ test: /creator-attribution|creator attribution/i, icon: BadgeCheck },
|
|
35
|
+
{ test: /submit-modal|submit modal/i, icon: Send },
|
|
36
|
+
{ test: /mobile-panel-motion|mobile panel motion/i, icon: PanelBottomOpen },
|
|
37
|
+
{ test: /mobile-picker-layout|mobile picker layout/i, icon: LayoutList },
|
|
38
|
+
{
|
|
39
|
+
test: /invite-follow-up-shape-layout|follow-up shape layout/i,
|
|
40
|
+
icon: LayoutTemplate,
|
|
41
|
+
},
|
|
42
|
+
{ test: /invite-animations|invite animations/i, icon: Sparkles },
|
|
43
|
+
{ test: /shape-color-picker|shape & color picker/i, icon: Palette },
|
|
44
|
+
{ test: /proto-shapes|proto shapes/i, icon: Hexagon },
|
|
45
|
+
{ test: /invite-copy|invite copy/i, icon: MessageSquareText },
|
|
22
46
|
{ test: /retention|expir|before/i, icon: Clock },
|
|
23
47
|
{ test: /calendar|date.?picker|date-range/i, icon: CalendarDays },
|
|
24
48
|
{ test: /mobile/i, icon: Smartphone },
|
package/src/lib/tool-portal.ts
CHANGED
|
@@ -8,6 +8,9 @@ export const PROTOTYPE_VIEWPORT_ID = "prototype-viewport";
|
|
|
8
8
|
|
|
9
9
|
export const PROTOTYPE_SCREENSHOT_ATTR = "data-prototype-screenshot";
|
|
10
10
|
|
|
11
|
+
/** Synced source-app previews (component library, etc.) — fixed product tokens, not tool theme. */
|
|
12
|
+
export const PROTOTYPE_SOURCE_SURFACE_ATTR = "data-prototype-source-surface";
|
|
13
|
+
|
|
11
14
|
export const PROTOTYPE_COMMENTS_SIDEBAR_ROOT_ID =
|
|
12
15
|
"prototype-comments-sidebar-root";
|
|
13
16
|
|
|
@@ -16,6 +19,27 @@ export function getPrototypePortalContainer(): HTMLElement | undefined {
|
|
|
16
19
|
return document.getElementById(PROTOTYPE_ROOT_ID) ?? undefined;
|
|
17
20
|
}
|
|
18
21
|
|
|
22
|
+
/** Dialogs/overlays inside prototype content — scoped to the preview viewport, not the review sidebar. */
|
|
23
|
+
export function getPrototypeDialogPortalContainer(): HTMLElement | undefined {
|
|
24
|
+
if (typeof document === "undefined") return undefined;
|
|
25
|
+
return (
|
|
26
|
+
document.getElementById(PROTOTYPE_VIEWPORT_ID) ??
|
|
27
|
+
document.getElementById(PROTOTYPE_PREVIEW_STAGE_ID) ??
|
|
28
|
+
document.getElementById(PROTOTYPE_ROOT_ID) ??
|
|
29
|
+
undefined
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Tool chrome modals (prompt copy, link source, etc.) — outside the screenshot surface so dark/light tool theme applies. */
|
|
34
|
+
export function getPrototypeToolDialogPortalContainer(): HTMLElement | undefined {
|
|
35
|
+
if (typeof document === "undefined") return undefined;
|
|
36
|
+
return (
|
|
37
|
+
document.getElementById(PROTOTYPE_ROOT_ID) ??
|
|
38
|
+
document.getElementById(PROTOTYPE_PREVIEW_STAGE_ID) ??
|
|
39
|
+
undefined
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
19
43
|
export function getPrototypePreviewStage(): HTMLElement | undefined {
|
|
20
44
|
if (typeof document === "undefined") return undefined;
|
|
21
45
|
return document.getElementById(PROTOTYPE_PREVIEW_STAGE_ID) ?? undefined;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { copyTextWithAttachments } from "@prototype/lib/copy-text-with-attachments";
|
|
3
4
|
import { cn } from "@prototype/lib/utils";
|
|
4
5
|
import { CheckIcon, CopyIcon, SaveIcon } from "lucide-react";
|
|
5
6
|
import { createElement, useCallback, useRef, useState } from "react";
|
|
6
|
-
import { toast } from "sonner";
|
|
7
7
|
|
|
8
8
|
type CopyState = "idle" | "scaling-out" | "copied" | "reverting-out" | null;
|
|
9
9
|
|
|
@@ -25,7 +25,7 @@ export default function useCopyToClipboard({
|
|
|
25
25
|
const isCopied = state === "copied" || state === "reverting-out";
|
|
26
26
|
|
|
27
27
|
const copy = useCallback(
|
|
28
|
-
(text: string) => {
|
|
28
|
+
async (text: string) => {
|
|
29
29
|
clearTimeout(timerRef.current);
|
|
30
30
|
setState("idle");
|
|
31
31
|
|
|
@@ -43,17 +43,11 @@ export default function useCopyToClipboard({
|
|
|
43
43
|
timerRef.current = setTimeout(revert, cooldownTime);
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
});
|
|
52
|
-
})
|
|
53
|
-
.finally(() => {
|
|
54
|
-
setState("scaling-out");
|
|
55
|
-
timerRef.current = setTimeout(copied, SCALE_OUT_MS);
|
|
56
|
-
});
|
|
46
|
+
const didCopy = await copyTextWithAttachments(text);
|
|
47
|
+
if (!didCopy) return;
|
|
48
|
+
|
|
49
|
+
setState("scaling-out");
|
|
50
|
+
timerRef.current = setTimeout(copied, SCALE_OUT_MS);
|
|
57
51
|
},
|
|
58
52
|
[cooldownTime],
|
|
59
53
|
);
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
createPrototypeChangelogMetaRoute,
|
|
7
7
|
createPrototypeCommentIdRoute,
|
|
8
8
|
createPrototypeCommentsRoute,
|
|
9
|
-
createPrototypeDesignExplorationDefaultRoute,
|
|
10
9
|
createPrototypeScreenshotRoute,
|
|
11
10
|
} from "./create-prototype-api-routes";
|
|
12
11
|
import { createGalleryApi } from "./gallery/create-gallery-api";
|
|
12
|
+
import { GET as getPrototypeStorageStatus } from "./prototype-storage-status-route";
|
|
13
13
|
import { GET as getPrVercelPreview } from "./pr-vercel-preview-route";
|
|
14
14
|
|
|
15
15
|
type ApiRouteContext = {
|
|
@@ -20,8 +20,6 @@ export function createPrototypeApiRoute(_config: PrototypeConfig) {
|
|
|
20
20
|
const commentsRoute = createPrototypeCommentsRoute(_config);
|
|
21
21
|
const commentIdRoute = createPrototypeCommentIdRoute(_config);
|
|
22
22
|
const changelogMetaRoute = createPrototypeChangelogMetaRoute(_config);
|
|
23
|
-
const designExplorationDefaultRoute =
|
|
24
|
-
createPrototypeDesignExplorationDefaultRoute(_config);
|
|
25
23
|
const screenshotRoute = createPrototypeScreenshotRoute(_config);
|
|
26
24
|
const galleryApi =
|
|
27
25
|
_config.gallery === false ? null : createGalleryApi(_config.gallery);
|
|
@@ -44,6 +42,12 @@ export function createPrototypeApiRoute(_config: PrototypeConfig) {
|
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
|
|
45
|
+
if (path[0] === "prototypes" && path.length === 2 && path[1] === "storage-status") {
|
|
46
|
+
if (method === "GET") {
|
|
47
|
+
return getPrototypeStorageStatus();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
47
51
|
if (path[0] === "prototypes" && path.length >= 2) {
|
|
48
52
|
const slug = path[1]!;
|
|
49
53
|
|
|
@@ -72,19 +76,6 @@ export function createPrototypeApiRoute(_config: PrototypeConfig) {
|
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
|
|
75
|
-
if (path.length === 3 && path[2] === "design-exploration-default") {
|
|
76
|
-
if (method === "GET") {
|
|
77
|
-
return designExplorationDefaultRoute.GET(request, {
|
|
78
|
-
params: Promise.resolve({ slug }),
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
if (method === "PUT") {
|
|
82
|
-
return designExplorationDefaultRoute.PUT(request, {
|
|
83
|
-
params: Promise.resolve({ slug }),
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
79
|
if (path.length === 3 && path[2] === "screenshot" && method === "POST") {
|
|
89
80
|
return screenshotRoute.POST(request, { params: Promise.resolve({ slug }) });
|
|
90
81
|
}
|
|
@@ -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,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
|
@@ -12,7 +12,6 @@ export {
|
|
|
12
12
|
createPrototypeChangelogMetaRoute,
|
|
13
13
|
createPrototypeCommentIdRoute,
|
|
14
14
|
createPrototypeCommentsRoute,
|
|
15
|
-
createPrototypeDesignExplorationDefaultRoute,
|
|
16
15
|
createPrototypeScreenshotRoute,
|
|
17
16
|
} from "./server/create-prototype-api-routes";
|
|
18
17
|
|
|
@@ -42,10 +41,3 @@ export {
|
|
|
42
41
|
getPrototypeChangelogMeta,
|
|
43
42
|
setPrototypeChangelogMeta,
|
|
44
43
|
} 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";
|