diff --git a/.env.example b/.env.example index bc41989..0d485f8 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ # Server only. Never put this key in browser scripts or commit your .env file. APIMART_API_KEY= STUDIO_PUBLIC_ORIGIN=https://component.wrnexusjs.dev -# Design Studio uses the gpt-6-luna model at https://api.apimart.ai/v1/chat/completions. +# Change the model here, then restart the server. +APIMART_MODEL=gpt-5-mini +# APIMart endpoint: https://api.apimart.ai/v1/chat/completions. diff --git a/README.md b/README.md index 847d423..7456d0d 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ APIMART_API_KEY=your_apimart_key Restart the app after setting the key. No key goes in the browser or application configuration. The server calls https://api.apimart.ai/v1/chat/completions with -model `gpt-6-luna`, bearer authentication, and `stream: false`, as described in +model `gpt-5-mini`, bearer authentication, and `stream: false`, as described in https://docs.apimart.ai/en/api-reference/texts/general/chat-completions. Model access must be available on your APIMart account. No live provider call is part of the normal build or test suite. @@ -191,3 +191,6 @@ component gallery page. ### Interactive Studio gallery Choose Component gallery in the preview Content menu, then select pages 1–7. Each of the 104 entries has populated sample data and local icons. Controls use their shipped browser functions; charts and workflow graphs include their real runtimes. Sample events appear below each example. Preview links stay in the demo; forms and uploads do not submit data. Switch tracks stay rounded across presets and existing drafts. The editor reports when the preview is rendering, ready or unable to load. + +Manual design works without an API key: choose a preset or Create new, edit light/dark colors, fonts, corner radii, spacing and shadows, select Update preview, then export under Review & apply. Undo and decision locks protect changes. +Set `APIMART_MODEL=gpt-5-mini` in the server `.env` to choose the AI model. Restart the server after changing it; the Studio displays the active model. diff --git a/app/lib/studio-ai.ts b/app/lib/studio-ai.ts index 90c36c1..68ba802 100644 --- a/app/lib/studio-ai.ts +++ b/app/lib/studio-ai.ts @@ -3,7 +3,7 @@ import type { GenerationRequest, } from "@wrnexus/design-studio"; -export const APIMART_MODEL = "gpt-6-luna"; +export const APIMART_MODEL = "gpt-5-mini"; export const AI_TIMEOUT_MS = 60_000; export const AI_OUTPUT_TOKENS = 8192; type FailureKind = @@ -22,7 +22,7 @@ const failureMessages: Record = { quota: "APIMart reports insufficient credit or quota. Check the provider account balance and limits.", model: - "APIMart could not access gpt-6-luna. Check that this model is enabled for the server API key.", + "APIMart could not access the configured model. Check that this model is enabled for the server API key.", rate: "APIMart is rate limiting requests. Wait a minute and generate again.", unavailable: "APIMart is temporarily unavailable. Try generating again shortly.", @@ -70,6 +70,7 @@ function failureFor(status: number, code?: unknown) { export function apimartProvider( apiKey: string, transport: typeof fetch = fetch, + model = APIMART_MODEL, ): GenerationProvider { return { name: "APIMart", @@ -87,7 +88,7 @@ export function apimartProvider( "content-type": "application/json", }, body: JSON.stringify({ - model: APIMART_MODEL, + model, stream: false, max_tokens: options.maxTokens ?? AI_OUTPUT_TOKENS, messages: [ @@ -137,7 +138,7 @@ export function apimartProvider( return { value: choice.message.content, provider: "APIMart", - model: APIMART_MODEL, + model, }; }, }; @@ -166,7 +167,11 @@ async function readBounded(source: Request | Response, limit: number) { } } -function synchronousStudioAI(apiKey?: string, transport: typeof fetch = fetch) { +function synchronousStudioAI( + apiKey?: string, + transport: typeof fetch = fetch, + model = APIMART_MODEL, +) { let active = 0, windowStart = Date.now(), count = 0; @@ -198,7 +203,7 @@ function synchronousStudioAI(apiKey?: string, transport: typeof fetch = fetch) { if (url.pathname.endsWith("/generation") && request.method === "GET") return json({ provider: "APIMart", - model: APIMART_MODEL, + model, templateVersion: 1, delivery: "polling", maxAttempts: 2, @@ -236,7 +241,7 @@ function synchronousStudioAI(apiKey?: string, transport: typeof fetch = fetch) { let calls = 0; let exhausted = false; let failure: APIMartFailure | undefined; - const upstream = apimartProvider(apiKey, transport); + const upstream = apimartProvider(apiKey, transport, model); const provider: GenerationProvider = { name: upstream.name, async generate(prompt, options) { @@ -266,7 +271,7 @@ function synchronousStudioAI(apiKey?: string, transport: typeof fetch = fetch) { const generate = () => generateDesigns(input, { provider, - model: APIMART_MODEL, + model, signal: deadline, timeoutMs: AI_TIMEOUT_MS, maxOutputTokens: AI_OUTPUT_TOKENS, @@ -351,8 +356,12 @@ function synchronousStudioAI(apiKey?: string, transport: typeof fetch = fetch) { } /** Short HTTP requests keep the deployment proxy out of the provider wait. */ -export function studioAI(apiKey?: string, transport: typeof fetch = fetch) { - const run = synchronousStudioAI(apiKey, transport); +export function studioAI( + apiKey?: string, + transport: typeof fetch = fetch, + model = APIMART_MODEL, +) { + const run = synchronousStudioAI(apiKey, transport, model); type Job = { status: "pending" | "complete" | "failed" | "cancelled"; controller: AbortController; diff --git a/app/lib/studio-guide.ts b/app/lib/studio-guide.ts index 1d23d90..9aeecdb 100644 --- a/app/lib/studio-guide.ts +++ b/app/lib/studio-guide.ts @@ -13,13 +13,24 @@ export const studioHelpScript = `(() => { window.addEventListener("unhandledrejection", fail); document.addEventListener("DOMContentLoaded", () => { if (failed) report(); - const button = document.querySelector('[data-tab="guided"]'); + const button = document.querySelector('[data-tab="manual"]'); if (button) button.click(); }); })();`; -export function addStudioGuide(html: string, configured: boolean) { - const guide = `
How to use Design Studio
  1. Describe your website. Open Guided design, enter your product and audience, then choose colors, shapes and spacing with Next.
  2. Generate and compare. Click Generate three directions. The first valid alternative appears in the large preview. Review another to compare, then choose Use reviewed proposal to save it. Show current draft returns without accepting.
  3. Refine it. Try “make buttons square and use blue as the primary color” under Edit with words. Lock decisions you want to keep.
  4. Use it in your app. Open Review & apply and export Theme config or Compiled CSS. This website previews and exports designs; it does not change your deployed app automatically.

${configured ? "APIMart AI is enabled with gpt-6-luna. Generation sends your brief and current design to APIMart only when you request it." : "AI is not configured. Local presets and supported rule-based edits still work. Set APIMART_API_KEY in the server .env and restart to enable gpt-6-luna."}

`; +export function addStudioGuide( + html: string, + configured: boolean, + model = "gpt-5-mini", +) { + const safeModel = model.replace( + /[&<>"']/g, + (char) => + ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[ + char + ]!, + ); + const guide = `
How to use Design Studio
  1. Build manually. Open Manual design to choose colors, fonts, corner radii, spacing and shadows. Switch light/dark modes, select Update preview to save settings, then export. No AI is required.
  2. Describe your website for AI. Open Guided design, enter your product and audience, then choose colors, shapes and spacing with Next.
  3. Generate and compare. Click Generate three directions. The first valid alternative appears in the large preview. Review another to compare, then choose Use reviewed proposal to save it. Show current draft returns without accepting.
  4. Refine it. Try “make buttons square and use blue as the primary color” under Edit with words. Lock decisions you want to keep.
  5. Use it in your app. Open Review & apply and export Theme config or Compiled CSS. This website previews and exports designs; it does not change your deployed app automatically.

${configured ? `APIMart AI is enabled with ${safeModel}. Generation sends your brief and current design to APIMart only when you request it.` : "AI is not configured. Local presets and supported rule-based edits still work. Set APIMART_API_KEY and APIMART_MODEL in the server .env and restart to enable AI."}

`; return html .replace( '