869e2bb75b
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
68 lines
11 KiB
Markdown
68 lines
11 KiB
Markdown
# Findings: Server-Backed Brand Kit Canvas Context
|
|
|
|
## Requirements
|
|
- The previous implementation is frontend-only and insufficient for durable user ownership.
|
|
- The canvas header needs a compact Brand Kit selector beside the project name.
|
|
- The selector contains None plus the authenticated user's Brand Kits and shows the active selection.
|
|
- Agent Chat and every new session within that canvas must use the selected Brand Kit as a creative baseline.
|
|
- The Home prompt composer also needs a Brand Kit picker. Its empty state provides a direct `创建 Brand Kit` action.
|
|
|
|
## Initial Decisions
|
|
- Persist the project-to-kit relation on the server and inject context server-side so clients cannot silently omit it.
|
|
- Preserve the existing restrained Moteva canvas visual language and use the supplied interaction hierarchy without copying product identifiers.
|
|
|
|
## Repository Discovery
|
|
- The server is an existing go-zero REST service with one `img_infinite_canvas.api`, generated handlers/types, thin logic files, and a shared `ServiceContext`.
|
|
- PostgreSQL persistence is SQLC-based (`server/sqlc.yaml`, `schema.sql`, `query.sql`) while memory storage supports local development and tests.
|
|
- The local `mcp-zero` binary and `/Users/liangxu/go/bin/goctl` are both available; API generation can follow the required spec-first workflow.
|
|
- The frontend Brand Kit implementation is still uncommitted and browser-local, so it must be migrated carefully without discarding its editor/model work.
|
|
- Playwright prerequisites are available, and the user has an authenticated browser session for final integration testing.
|
|
- `design.Project` is the common object passed through chat planning, conversation, and image-prompt generation, so adding resolved Brand Kit context there gives one reliable injection point for all Agent Chat flows.
|
|
- `design.Repository` currently owns project CRUD; both memory and PostgreSQL implementations enforce user scope through `design.UserIDFromContext`.
|
|
- Projects currently persist title/canvas/thread state but no `brandKitId`; SQLC generates PostgreSQL accessors from `schema.sql` and `query.sql`.
|
|
- Agent messages already carry `thread_id`, and the request can start a new thread; the selected kit should therefore be loaded from the project before every chat operation rather than copied into individual messages.
|
|
- `DesignService.AgentChat` reloads the project before planning every turn, then passes project-scoped copies to planning, research, conversation, and background generation. Hydrating the Brand Kit immediately after that load covers existing and newly created threads.
|
|
- Creative-agent prompt builders already centralize project identity through `projectBriefContext`, while deterministic/fallback image generation reads `design.Project`; Brand Kit context should be added to both centralized context builders and agent memory.
|
|
- Project creation has synchronous and asynchronous paths. Both should accept an optional requested kit and otherwise bind the authenticated user's default kit server-side.
|
|
- An explicit Home selection should override the default kit; the server must still verify that the selected kit belongs to the authenticated user.
|
|
- Home's composer has a compact action row with existing Radix Popovers for model selection, making a palette-icon Brand Kit popover a native extension rather than a new interaction pattern.
|
|
- Home currently appends a browser-local default kit prompt. This must be removed once project creation sends `brandKitId`, preventing duplicated or client-controlled brand instructions.
|
|
- Frontend `Project` and canvas snapshot merge paths need to preserve `brandKitId`; the direct project-document endpoint remains the authoritative source.
|
|
- Canvas workspace already centralizes title rendering and project updates, so the selector can live beside `WorkspaceTitle` and update the same in-memory project after a binding API call.
|
|
- The existing Brand Kit document is already versioned and contains the requested grouped colors, uploaded fonts with optional sizes/descriptions, logos, cover, references, voice, and visual direction. The server can validate this exact version-1 shape and compile it into authoritative prompt context.
|
|
- The project repository abstraction has a single `Save` operation and user-scoped `Get`; adding `BrandKitID` to `design.Project` will naturally flow through memory/cache/realtime wrappers as long as PostgreSQL mapping and API mappers are updated.
|
|
- `ServiceContext` constructs storage-specific modules beside the project repository. A dedicated Brand Kit service/store should follow the existing auth/sharing module pattern and use the same configured memory/PostgreSQL driver.
|
|
- All `/api/*` routes except the explicit public allowlist already require a valid bearer session, so new `/api/brand-kits` routes automatically enforce login before logic runs.
|
|
- SQLC is installed locally. The existing PostgreSQL repository runs the idempotent schema at startup, making schema/query changes plus SQLC regeneration the consistent persistence path.
|
|
- Direct image mode currently sends the raw user prompt to image generation, bypassing the creative planner. Brand context therefore must also be added to the direct/fallback image prompt builders, not only long-term memory and `projectBriefContext`.
|
|
- Asynchronous create/follow-up jobs reload projects from persistence, so Brand Kit context must be re-resolved inside those job execution paths; hydrating only the initial HTTP turn would be insufficient.
|
|
- Uploaded Brand Kit logos, cover images, and reference images are resolved server-side as ephemeral project inputs. Image generation appends them after user-supplied references so existing inline reference numbering remains stable.
|
|
|
|
## Device Management Request (2026-07-11)
|
|
- The requested visual is a restrained account settings page headed `设备管理`, with a safety-limit explanation, a bulk removal action, and a table of active devices.
|
|
- The repository already contains `list_account_devices_logic.go`, `remove_account_devices_logic.go`, and an `AccountManagementDialog`, so the first implementation step is to verify and complete the existing vertical slice instead of creating a parallel settings system.
|
|
- The target interaction must cover loading, API failure, no secondary devices, current-device labeling, disabled destructive actions, confirmation, and responsive behavior.
|
|
- The account dialog already renders a device section and calls `authGateway.devices()` / `removeDevices()`, but it silently fabricates a current-device row whenever the API is empty or fails. That masks production errors and makes destructive-action state unreliable.
|
|
- The existing removal handler has no pending/error guard around the async request, so repeated confirmation clicks or a network failure can produce duplicate calls or an unhandled rejection.
|
|
- Existing localized copy says bulk removal invalidates the current session, while the visible button is disabled when only the current device remains. Backend behavior must be treated as the source of truth and the copy aligned to it.
|
|
- The server device service is currently a stub: `ListDevices` always synthesizes one unknown desktop row and both `RemoveOtherDevices` and `Logout` return zero. There is no persistent session/device registry.
|
|
- Access and refresh tokens currently identify only the user and token type. Production-grade selective revocation therefore requires a random session ID claim plus a server-side session record; user-only stateless tokens cannot distinguish the current device from other logins.
|
|
- The desired bulk action is already named `RemoveOtherDevices` and the UI disables it when only the current session exists, so the consistent contract is: preserve the active session, revoke every other session, and update confirmation copy accordingly.
|
|
- The authentication store supports users and verification codes only. A complete implementation must add session persistence to both memory and PostgreSQL stores, bind token validation to active session state, and capture request/device metadata without putting HTTP concerns into business logic.
|
|
- Selected architecture: signed access/refresh tokens receive an opaque random `sid`; active sessions are persisted with user, device, creation, expiry, last-seen, and revocation timestamps; every bearer authentication checks the session registry.
|
|
- Backward compatibility: an otherwise-valid legacy access token without `sid` is mapped to a stable digest-derived session only after confirming that its subject is an existing user. This avoids a forced logout during rollout and prevents signed state tokens from becoming user sessions.
|
|
- Middleware will attach sanitized request metadata for public login routes and authenticated requests, then attach the resolved session ID to request context. Business logic remains HTTP-agnostic.
|
|
- Last-seen writes will be throttled and active device rows sorted current-first, then most recently seen. Device metadata is informational only.
|
|
- PostgreSQL uses the repository's existing idempotent embedded schema; memory and PostgreSQL stores will implement the same atomic revoke-others contract.
|
|
- `github.com/mileusna/useragent` is now the selected parser. It exposes normalized browser, OS, device, desktop/mobile/tablet, and bot fields while keeping authorization independent from user-agent data.
|
|
- The Next.js API proxy forwards incoming headers after removing only hop-by-hop headers, so browser UA and Client Hint metadata reach the Go middleware without a new client contract.
|
|
- The current device table is rendered as three independent columns rather than semantic rows. This makes loading/error/empty states awkward, can misalign cells, and forces horizontal scrolling on mobile.
|
|
- The dialog already has restrained neutral styling consistent with the supplied reference. The right approach is to preserve that language while replacing only the device slice with a row grid, explicit async state machine, accessible status text, and mobile stacking.
|
|
- Current helper text fabricates an extra empty separator in the device string. The replacement will expose normalized primary metadata and a quieter client/last-seen line instead of leaking placeholder delimiters.
|
|
- Follow-up requirement: the desktop and mobile session counts must be configurable. The backend config will be the source of truth, `GET /api/account/devices` will return the effective limits, and localized UI copy will interpolate those values.
|
|
- Bug confirmation: `TestDesktopDeviceLimitRevokesOldestSession` fails because `upsertAndSign` creates sessions without consulting `DeviceLimits`; the values currently affect only API/UI presentation.
|
|
- Enforcement policy: limits apply independently to normalized desktop and mobile sessions. A new login succeeds and atomically revokes the oldest same-type session, so the latest login wins and active sessions never exceed the configured count.
|
|
- Existing sessions created before enforcement are reconciled when the authenticated device list is read, preserving the current session first and then the most recently created sessions.
|
|
- Follow-up display requirement: `lastSeenAt` must be a timezone-neutral instant over the API. The backend will emit RFC3339 UTC and the browser will format it with `Intl.DateTimeFormat` using its local timezone and the selected UI locale.
|
|
- No configured `mcp-zero` callable tool is available in this session, so API validation/generation will use the documented `goctl` fallback after the `.api` spec is updated.
|