Prompt nodes now accept TXT, Markdown, and DOCX uploads while preserving the node contract as plain prompt text. The canvas preview shows imported Markdown as a lightweight document card, and DOCX extraction stays local to body text so generation inputs remain deterministic.
Constraint: Text-class nodes must still output plain text into existing workflow edges.
Rejected: Add a full document editor/parser layer | too broad for the requested node-level upload and preview behavior.
Confidence: high
Scope-risk: moderate
Directive: Keep uploaded document data normalized to prompt text unless downstream nodes explicitly gain rich document inputs.
Tested: npm run test:run -- src/components/__tests__/PromptNode.test.tsx src/components/modals/__tests__/MarkdownEditorModal.test.tsx src/utils/__tests__/textDocumentImport.test.ts --reporter=dot
Tested: npm run build
Tested: git diff --check
Not-tested: npm run lint still fails because Next 16 treats the existing next lint script as a /lint project path.
The provider branch makes model defaults and Popi metadata resolution more asynchronous, while the local UI keeps duplicate node-body settings hidden. This keeps pricing available when metadata is already complete, avoids refreshing placeholder Popi selections, preserves explicit Popi schema hydration, and fixes strict TypeScript narrowing exposed by the merged media upload route.
Constraint: Preserve hidden node-body settings while keeping Popi provider schema hydration available.
Rejected: Re-enable inline node settings for all external providers | would reintroduce duplicated controls the user asked to hide.
Confidence: high
Scope-risk: moderate
Directive: Do not fetch per-model schemas for hidden non-Popi node settings unless an explicit modelSchemaRequestId requests it.
Tested: npm run test:run -- src/components/__tests__/PromptNode.test.tsx src/components/__tests__/WorkflowCanvas.test.tsx src/components/modals/__tests__/MarkdownEditorModal.test.tsx src/components/__tests__/FloatingNodeHeader.test.tsx src/components/__tests__/LLMGenerateNode.test.tsx src/components/__tests__/GenerateImageNode.test.tsx src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerationComposer.test.tsx src/app/api/generate/providers/__tests__/popiserver.test.ts src/app/api/__tests__/popiserverModels.test.ts --reporter=dot
Tested: npm run build
Tested: git diff --check
Tested: rg -n '^(<<<<<<<|=======|>>>>>>>)' src
Not-tested: npm run lint cannot run because the existing Next 16 script maps next lint to a missing ./lint project directory.
Multi-image NewAPIWG generations were sending base64 reference images
inside the /api/generate JSON body. Two ordinary canvas references can
push that payload near the dev/server body boundary, leaving the route
to parse an incomplete JSON string and surface a raw syntax error.
Move large NewAPIWG references through the existing in-memory image
serving path: the browser uploads large data URLs as multipart image
files, sends short /api/images/{id} URLs in the generation request, and
the route resolves those temporary URLs back to server-local image data
before calling providers. The route also turns malformed/truncated JSON
into a readable validation error.
Constraint: Keep provider payload semantics unchanged after /api/generate receives the request
Constraint: Do not rely on request origin matching because Next dev can report 0.0.0.0 while the browser uses localhost
Rejected: Increase body size limits only | still sends multi-MB JSON and does not protect hosted/proxied environments
Rejected: Compress reference images client-side | changes model input fidelity and still leaves large JSON possible
Confidence: high
Scope-risk: moderate
Directive: Do not forward /api/images/{id} temporary URLs to external providers; resolve them server-side first
Tested: npm run test:run -- src/store/execution/__tests__/nanoBananaExecutor.test.ts src/app/api/generate/__tests__/route.test.ts
Tested: npm run build
Not-tested: Live NewAPIWG generation with the user's exact two images
The welcome screen now matches the Popi.TV brand and gives unauthenticated users two explicit paths: jump to the main-site login page or provide a Popi/NewAPI gateway key directly. The manually provided key is persisted in the local user store and forwarded through the NewAPI-backed generation, model, LLM, and chat routes without requiring a login token.
Constraint: Users need a usable local path when they are not logged into the main site.\nRejected: Keep the startup login modal for missing tokens | it blocked the in-welcome login/key choices.\nRejected: Rename internal workflow prompts and storage keys | those are compatibility/internal identifiers, not the visible brand surface.\nConfidence: high\nScope-risk: moderate\nDirective: Keep manual gateway-key access scoped to NewAPI-backed paths; non-NewAPI providers should continue requiring a login token.\nTested: npm run test:run -- src/app/api/models/__tests__/route.test.ts src/app/api/models/[modelId]/__tests__/route.test.ts src/app/api/llm/__tests__/route.test.ts src/app/api/chat/__tests__/route.test.ts src/app/api/generate/__tests__/route.test.ts\nTested: npm run test:run -- src/components/__tests__/QuickstartInitialView.test.tsx src/components/__tests__/WelcomeModal.test.tsx\nTested: npm run build\nTested: Browser verification for Popi.TV welcome title and two unauthenticated entry paths\nTested: git diff --cached --check\nNot-tested: Full test suite\nNot-tested: npm run lint still fails because the existing Next 16 next lint script resolves /lint as a project directory
The welcome screen now matches the Popi.TV brand and gives unauthenticated users two explicit paths: jump to the main-site login page or provide a Popi/NewAPI gateway key directly. The manually provided key is persisted in the local user store and forwarded through the NewAPI-backed generation, model, LLM, and chat routes without requiring a login token.
Constraint: Users need a usable local path when they are not logged into the main site.\nRejected: Keep the startup login modal for missing tokens | it blocked the in-welcome login/key choices.\nRejected: Rename internal workflow prompts and storage keys | those are compatibility/internal identifiers, not the visible brand surface.\nConfidence: high\nScope-risk: moderate\nDirective: Keep manual gateway-key access scoped to NewAPI-backed paths; non-NewAPI providers should continue requiring a login token.\nTested: npm run test:run -- src/app/api/models/__tests__/route.test.ts src/app/api/models/[modelId]/__tests__/route.test.ts src/app/api/llm/__tests__/route.test.ts src/app/api/chat/__tests__/route.test.ts src/app/api/generate/__tests__/route.test.ts\nTested: npm run test:run -- src/components/__tests__/QuickstartInitialView.test.tsx src/components/__tests__/WelcomeModal.test.tsx\nTested: npm run build\nTested: Browser verification for Popi.TV welcome title and two unauthenticated entry paths\nTested: git diff --cached --check\nNot-tested: Full test suite\nNot-tested: npm run lint still fails because the existing Next 16 next lint script resolves /lint as a project directory
The app now starts I18nProvider in zh-CN when no saved language exists, while preserving explicit saved choices. The login-required Ant Design modal also uses the v6 mask.closable shape so the console no longer warns about maskClosable.
Constraint: First-run locale should not override an existing saved language preference
Constraint: Ant Design 6 deprecates maskClosable on Modal.confirm
Rejected: Keep browser-language detection | first entry needs deterministic Simplified Chinese
Confidence: high
Scope-risk: narrow
Directive: Keep provider-less useI18n fallback stable for isolated tests unless all callers are wrapped in I18nProvider
Tested: npm run test:run -- src/i18n/__tests__/index.test.tsx src/components/__tests__/TemplateExplorerView.test.tsx src/components/__tests__/Header.test.tsx src/utils/__tests__/loginRequiredModal.test.ts src/utils/__tests__/authFetch.test.ts src/store/__tests__/userStore.test.ts
Tested: npm run build
Not-tested: npm run lint | existing Next 16 next lint script resolves to nonexistent /lint directory
Browser-file-system workflow saves were still bypassing external media storage, so local projects could keep large image, video, and audio data embedded instead of writing refs into the selected browser-backed workspace. The storage path now uses the browser file helpers for image, video, and audio refs and hydrates them back without API calls.
Constraint: Keep local browserfs storage on the existing external media storage path instead of adding a new persistence mode.
Rejected: Keep browserfs excluded from externalization | it preserves the old large-payload behavior for local projects.
Confidence: high
Scope-risk: moderate
Directive: Do not route browserfs media through server API endpoints; browserfs paths must stay client-local.
Tested: npm run test:run
Tested: npm run build
Tested: git diff --check -- src/store/workflowStore.ts src/utils/browserFileSystem.ts src/utils/mediaStorage.ts src/utils/__tests__/mediaStorage.browserfs.test.ts
Not-tested: npm run lint is blocked because next lint is no longer a valid Next 16 command in this repo
Not-tested: npx tsc --noEmit reports existing test-file type debt outside this change while next build TypeScript passes
Browser-file-system workflow saves were still bypassing external media storage, so local projects could keep large image, video, and audio data embedded instead of writing refs into the selected browser-backed workspace. The storage path now uses the browser file helpers for image, video, and audio refs and hydrates them back without API calls.
Constraint: Keep local browserfs storage on the existing external media storage path instead of adding a new persistence mode.
Rejected: Keep browserfs excluded from externalization | it preserves the old large-payload behavior for local projects.
Confidence: high
Scope-risk: moderate
Directive: Do not route browserfs media through server API endpoints; browserfs paths must stay client-local.
Tested: npm run test:run
Tested: npm run build
Tested: git diff --check -- src/store/workflowStore.ts src/utils/browserFileSystem.ts src/utils/mediaStorage.ts src/utils/__tests__/mediaStorage.browserfs.test.ts
Not-tested: npm run lint is blocked because next lint is no longer a valid Next 16 command in this repo
Not-tested: npx tsc --noEmit reports existing test-file type debt outside this change while next build TypeScript passes
The accumulated fixes move the test build toward the current product shape: Popi models are the default provider surface, remote community workflow APIs are removed from the local template flow, media nodes expose accurate preview/preview-modal behavior, and Generate Video owns first-class duration, resolution, and sound defaults instead of burying those values in provider schemas.
The test suite was brought back in line with the product mode split: Popi provider mode is the default runtime, while multi-provider model search remains covered explicitly under multi mode.
Constraint: Product direction is canvas-owned workflow structure with Popi Models as the tested default provider path.
Rejected: Keep legacy community workflow API surface | user requested removing remote data paths and retaining local templates.
Rejected: Treat video duration/resolution/sound as hidden provider params | these are user-facing Generate Video controls.
Confidence: high
Scope-risk: broad
Directive: Do not reintroduce remote community template loading without a product decision on hosted templates and i18n ownership.
Tested: npm test -- --run src/store/utils/__tests__/nodeDefaults.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerationComposer.test.tsx
Tested: npm run build
Tested: npm test -- --run src/components/__tests__/ModelSearchDialog.test.tsx src/store/utils/__tests__/localStorage.test.ts
Tested: npm run test:run
Not-tested: Live provider generation calls against paid external APIs.
New users now land on Popi gateway defaults that match the tested production-style path: APIYI Nano Banana 2 for images, Doubao Seedance for video, and Doubao Seed 2.0 Lite for LLM chat/tool use. Settings and FTUX screens render these effective defaults instead of empty placeholders, while existing local node defaults keep taking precedence.
The same pass keeps provider-overload errors user-facing by formatting the model name that the node currently displays, so fallback and switched-model failures are easier to understand across media nodes.
Constraint: Popi mode should hide internal NewApiWG naming and default to models available through the Popi gateway.
Rejected: Persist default values into localStorage for every new user | would make future system default changes harder and could overwrite explicit user preferences.
Confidence: high
Scope-risk: moderate
Directive: Do not change these defaults without checking the Popi gateway model ids and www.popi.art login/model-routing assumptions.
Tested: npm run test:run -- src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts src/components/__tests__/ProjectSetupModal.test.tsx src/components/__tests__/GenerationComposer.test.tsx src/app/api/chat/__tests__/route.test.ts src/components/__tests__/LLMGenerateNode.test.tsx src/utils/__tests__/userFacingErrors.test.ts
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build
Tested: git diff --check
Not-tested: npm run lint currently fails because next lint is no longer a valid project command in this Next.js setup
Default provider mode now exposes only Popi Models while preserving the existing multi-provider implementation behind an explicit multi mode. The model browser, project settings, onboarding, node badges, and model API all share the same provider-mode decision so the test deployment matches the www.popi.art login-aligned direction.
This also includes the pending UI and i18n node-label changes already present in the tracked worktree so the committed state matches the verified build.
Constraint: Product entry must align with www.popi.art by exposing Popi Models instead of internal NewApiWG naming
Constraint: Test deployment should match the full tracked working tree that passed build
Rejected: Delete legacy provider code | multi-provider internals remain useful behind PROVIDER_MODE=multi
Rejected: UI-only hiding | API and store layers now enforce Popi mode too
Confidence: high
Scope-risk: broad
Reversibility: clean
Directive: Keep NewApiWG as the internal provider id unless the gateway API/env contract is migrated
Tested: npm run test:run -- src/components/__tests__/ProjectSetupModal.test.tsx src/app/api/models/__tests__/route.test.ts src/lib/__tests__/llmModels.test.ts
Tested: npm run test:run -- src/components/__tests__/GenerateImageNode.test.tsx src/components/__tests__/GenerateVideoNode.test.tsx src/components/__tests__/GenerateAudioNode.test.tsx src/components/__tests__/Generate3DNode.test.tsx src/components/__tests__/LLMGenerateNode.test.tsx
Tested: npm run test:run -- src/lib/chat/tools.test.ts
Tested: PROVIDER_MODE=popi NEXT_PUBLIC_PROVIDER_MODE=popi npm run build
Tested: Browser smoke on http://localhost:3000 verified Project Settings and Model Search only expose Popi Models
Not-tested: Direct npx tsc --noEmit remains blocked by pre-existing test typing errors outside this change
P0 regressions showed several workflow paths needed to stay visible and deterministic: language switching had a hardcoded composer label, Video Trim hid controls before media loaded, and media handoffs to LLM needed real canvas edges rather than implicit conversion. P1 fixes keep canvas-owned structure by adding explicit image/video LLM handles, audio composer validation, 3D output/file routing, and Chat proposal cards that require user apply before mutating nodes.
Constraint: User asked to stop new feature work and only repair P0/P1 product-fit gaps.
Rejected: Auto-convert image or video directly into text for LLM | hides workflow structure and violates the visible-node product model.
Rejected: Let Chat tool calls silently add or edit nodes | canvas structure must be previewed and explicitly applied.
Confidence: high
Scope-risk: moderate
Directive: Keep media transformations represented by visible handles, nodes, and edges; do not add silent Chat or media mutation paths without product review.
Tested: Browser automation for language switch, video input to Generate Video, prompt to Generate Video, LLM default model, Video Trim controls, and video media to LLM input.
Tested: npm run test:run -- src/components/__tests__/ConnectionDropMenu.test.tsx src/components/__tests__/GenerationComposer.test.tsx src/components/__tests__/VideoTrimNode.test.tsx src/components/__tests__/LLMGenerateNode.test.tsx src/store/execution/__tests__/llmGenerateExecutor.test.ts src/app/api/llm/__tests__/route.test.ts src/store/execution/__tests__/generateAudioExecutor.test.ts src/store/execution/__tests__/generate3dExecutor.test.ts src/store/execution/__tests__/simpleNodeExecutors.test.ts src/components/__tests__/OutputNode.test.tsx src/components/__tests__/Generate3DNode.test.tsx src/components/__tests__/ChatPanel.test.tsx src/lib/chat/tools.test.ts src/lib/chat/editOperations.test.ts src/lib/__tests__/llmModels.test.ts src/store/utils/__tests__/nodeDefaults.test.ts
Tested: npm run build
Tested: git diff --cached --check
Not-tested: npm run lint, because the existing script calls removed Next.js lint command and exits with invalid project directory /lint.
Not-tested: Live paid provider calls for LLM, audio, and 3D generation.
The browser path helper briefly acted as a type predicate, which made existing non-browser path branches narrow to never under the production build. The callers now guard nullable paths explicitly while the helper stays a plain boolean check.
Constraint: ProjectSetupModal depends on checking both browserfs paths and regular filesystem strings in the same helper flow.
Confidence: high
Scope-risk: narrow
Tested: npm test -- --run src/components/__tests__/GenerateImageNode.test.tsx src/store/execution/__tests__/nanoBananaExecutor.test.ts src/components/__tests__/GenerationComposer.test.tsx
Tested: npx tsc --noEmit --pretty false filtered to touched browser path files
Not-tested: Full tsc remains blocked by existing test typing errors outside this change scope
Browser-local projects were deriving a null generations path, so carousel entries that only retained an image id could not resolve media during previous/next navigation. Generated images are now written into the browser-selected generations folder, project metadata keeps that browserfs generations path, and the image node can read browserfs generation files directly before falling back to the server load endpoint.
Constraint: Browser-selected folders cannot be read through server API routes.
Rejected: Keep relying on inline image history only | saved or migrated workflows can contain id-only history entries.
Confidence: high
Scope-risk: moderate
Directive: Browser-local media paths must stay on the File System Access API path, not server /api/load-generation.
Tested: npm test -- --run src/components/__tests__/GenerateImageNode.test.tsx src/store/execution/__tests__/nanoBananaExecutor.test.ts src/components/__tests__/GenerationComposer.test.tsx
Tested: npx tsc --noEmit --pretty false filtered to touched files
Not-tested: Full tsc remains blocked by existing test typing errors outside this change scope
The hosted app should not save each operator's project into the shared test server filesystem when the browser can grant local folder access. Project creation now requires a browser-selected local folder in supported browsers, remembers that folder as the project browser root, and the workflow browser can list and load projects from File System Access handles. Server filesystem APIs remain as a fallback for unsupported browsers.
Constraint: Hosted users need per-operator local project storage instead of shared server paths.
Rejected: Keep manual absolute path entry on supported browsers | it can silently target the server filesystem in the deployed app.
Confidence: high
Scope-risk: moderate
Directive: Do not re-enable server path entry for hosted browsers without adding per-user isolation and auth boundaries.
Tested: npm test -- --run src/components/__tests__/ProjectSetupModal.test.tsx src/components/__tests__/QuickstartTemplatesView.test.tsx src/store/utils/__tests__/localStorage.test.ts
Tested: npx tsc --noEmit --pretty false filtered to changed files
Provider video URLs can expire before users finish composing, and the bottom composer was too visually dominant for canvas work. This change keeps remote videos playable while background-localizing them, teaches video previews to use the media proxy, makes audio nodes first-class in the composer and executor, and adds a compact composer state for canvas visibility.
Constraint: Provider media URLs may be remote, signed, or CORS-restricted during playback and thumbnail extraction
Constraint: Audio generation nodes need to work from their internal prompt as well as connected text
Rejected: Store only provider URLs | remote URLs can expire and break later playback
Rejected: Treat audio nodes as unsupported composer nodes | it falls back to the image model and corrupts user intent
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep provider media playable through the resolver/proxy path and preserve local refs as the durable source when available
Tested: npm run test:run
Tested: npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx src/store/execution/__tests__/generateAudioExecutor.test.ts src/store/execution/__tests__/generateVideoExecutor.test.ts src/components/__tests__/VideoStitchNode.test.tsx src/components/__tests__/GenerateVideoNode.test.tsx
Tested: npm run build
Not-tested: npm run lint currently fails because next lint is not supported by this Next.js setup
Not-tested: npx tsc --noEmit still reports pre-existing test typing errors outside this change
Server-side native folder pickers cannot select a Windows user's local directory when the app is deployed remotely. This adds a browser File System Access path for supported browsers so the user can choose a local folder, create the project subfolder, and save the workflow JSON directly from the client.
Constraint: File System Access handles are browser-only and cannot be passed to server filesystem APIs
Constraint: External media folder storage still depends on server filesystem endpoints, so browser-local projects keep media embedded or remote in the workflow JSON
Rejected: Make /api/browse-directory choose the user's Windows folder | that endpoint runs on the server, not in the browser
Confidence: medium
Scope-risk: moderate
Directive: Extend the browser filesystem utility for local media inputs/generations before enabling external media storage for browserfs paths
Tested: npx vitest run src/components/__tests__/ProjectSetupModal.test.tsx src/components/__tests__/Header.test.tsx
Tested: npm run build
Not-tested: Manual Chrome/Edge File System Access flow on Windows test server