feat: remove Canvas inspiration entry
This commit is contained in:
1 parent
f8eee430f4
commit
b22559a596
11 files changed
+126
-98
No files matched your search
@@ -0,0 +1,102 @@
|
|||||||
|
# Task: Remove Canvas inspiration entry
|
||||||
|
|
||||||
|
## Identity
|
||||||
|
|
||||||
|
- Task ID: 20260906-remove-canvas-inspiration-56e64d28
|
||||||
|
- Mode: Feature
|
||||||
|
- Branch: codex/20260906-remove-canvas-inspiration-56e64d28-remove-canvas-inspiration
|
||||||
|
- Worktree: /Users/inmanx/Documents/makelore-task-remove-canvas-inspiration-56e64d28
|
||||||
|
- Base commit: f8eee430f4f4167dc2ecc682ece96c652cdde14b
|
||||||
|
- Owner: codex
|
||||||
|
- Status: Ready for integration
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Remove the user-visible “获取灵感” action from both expanded and collapsed
|
||||||
|
Canvas sidebars.
|
||||||
|
- Retire the Prompt Museum Renderer route and redirect historical
|
||||||
|
`/image-prompts` deep links to the Canvas workspace.
|
||||||
|
- Remove route-specific title-bar/sidebar treatment and update focused unit,
|
||||||
|
Electron E2E, README, and retained server-contract wording.
|
||||||
|
|
||||||
|
## Intent And Constraints
|
||||||
|
|
||||||
|
- Keep Canvas conversation, Living Form, Workspace, Quote, Task, Asset, project
|
||||||
|
creation, and project deletion behavior unchanged.
|
||||||
|
- Preserve Main-owned Prompt Museum API/auth/media security boundaries in this
|
||||||
|
small UI-removal task; do not infer authority to delete server data or change
|
||||||
|
backend contracts.
|
||||||
|
- Keep old links safe and deterministic by returning users to `/image-canvas`
|
||||||
|
without initializing Programming providers.
|
||||||
|
- Maintain the Chinese-only Makelore light interface and existing module-access
|
||||||
|
policy.
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
- Removed the lightbulb/inspiration action from both expanded and collapsed
|
||||||
|
Canvas sidebars.
|
||||||
|
- Retired the Prompt Museum Renderer route and its lazy bundle entry. Historical
|
||||||
|
`/image-prompts` paths now redirect to `/image-canvas` while remaining covered
|
||||||
|
by the Canvas module-access gate.
|
||||||
|
- Removed the obsolete Prompt Museum title-bar and sidebar-spacing special cases,
|
||||||
|
so Canvas keeps one workspace shell.
|
||||||
|
- Preserved the dormant Main-owned Prompt Museum Host API, authentication, media
|
||||||
|
validation, shared DTOs, and their existing focused tests; this task changes
|
||||||
|
only the AI Design product surface and does not delete server data.
|
||||||
|
- Updated README, retained server-contract wording, focused unit coverage, and
|
||||||
|
the Electron E2E flow to describe and verify the retired entry.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
- `corepack pnpm install --frozen-lockfile` — passed with repository-pinned pnpm
|
||||||
|
10.33.4.
|
||||||
|
- Focused Vitest coverage for App routing, Main layout, TitleBar, and the light
|
||||||
|
visual system — 4 files, 41 tests passed.
|
||||||
|
- `corepack pnpm run typecheck` — passed.
|
||||||
|
- `corepack pnpm run lint:check` — passed with zero errors and five pre-existing
|
||||||
|
warnings in unrelated Home/Makelore files.
|
||||||
|
- `corepack pnpm run build:vite` — passed for Renderer, Main, Preload, and the
|
||||||
|
release utility worker.
|
||||||
|
- `node ./node_modules/@playwright/test/cli.js test tests/e2e/image-workspace-v2.spec.ts --grep "removes the inspiration entry"`
|
||||||
|
— passed 1/1, confirming the sidebar entry is absent and the historical route
|
||||||
|
returns to Canvas.
|
||||||
|
- `corepack pnpm test` — passed: 1,859 ordinary tests passed, three skipped, and
|
||||||
|
the isolated pressure test passed 1/1.
|
||||||
|
- `git diff --check` — passed.
|
||||||
|
|
||||||
|
## Follow-ups
|
||||||
|
|
||||||
|
- A serialized Integration task should reconcile canonical Prompt Museum product
|
||||||
|
statements and split the mixed Museum/Canvas production-smoke commitment so
|
||||||
|
the still-relevant Canvas deletion and re-Quote checks remain tracked.
|
||||||
|
- Physically deleting the retained Main API, shared DTOs, or upstream data would
|
||||||
|
require a separate explicit scope; this task intentionally leaves those secure
|
||||||
|
boundaries intact and unreachable from the current product UI.
|
||||||
|
|
||||||
|
## Promotion Candidates
|
||||||
|
|
||||||
|
- Target: `.project-docs/30-worklog/current-state.md`,
|
||||||
|
`.project-docs/20-architecture/system-overview.md`,
|
||||||
|
`.project-docs/20-architecture/module-map.md`,
|
||||||
|
`.project-docs/20-architecture/data-flow.md`,
|
||||||
|
`.project-docs/40-domain/business-rules.md`,
|
||||||
|
`.project-docs/40-domain/glossary.md`, and
|
||||||
|
`.project-docs/80-commitments/commitments.md`.
|
||||||
|
- Proposal: record that Prompt Museum is no longer an active Canvas product
|
||||||
|
surface: expanded/collapsed sidebars expose no inspiration action, the page is
|
||||||
|
not mounted or bundled by App routing, and historical `/image-prompts` links
|
||||||
|
redirect to `/image-canvas`. Retained Main API/auth/media validation and shared
|
||||||
|
DTO code is dormant compatibility/security infrastructure rather than a visible
|
||||||
|
module. Remove Museum-specific production-smoke work from the mixed commitment
|
||||||
|
while retaining its Canvas deletion and re-Quote portions.
|
||||||
|
- Evidence: the user's explicit direction, focused 41-test regression suite,
|
||||||
|
full 1,859-test plus pressure pass, typecheck, lint, production build, and the
|
||||||
|
passing Electron route/sidebar check.
|
||||||
|
- Future impact: navigation and product documentation must not restore the
|
||||||
|
inspiration action or Prompt Museum route without a new explicit product
|
||||||
|
decision. Dormant API removal remains separately scoped.
|
||||||
|
- Semantic conflict: this supersedes canonical statements that Canvas exposes a
|
||||||
|
Prompt Museum entry and that its pending Prompt participates in active Canvas
|
||||||
|
data flow; it does not change ADR-007 or Main-owned security boundaries.
|
||||||
|
- Human confirmation: already supplied by the user in this task; no additional
|
||||||
|
product-direction approval is required during integration.
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
Makelore 是一个面向软件、视觉创作与智能机器人的 AI 桌面工作台。当前版本为 `2.0.0`,包含三个已开通产品模块。模块入口页采用统一的横向卡片视觉,工作区左上角入口点击后返回模块入口页:
|
Makelore 是一个面向软件、视觉创作与智能机器人的 AI 桌面工作台。当前版本为 `2.0.0`,包含三个已开通产品模块。模块入口页采用统一的横向卡片视觉,工作区左上角入口点击后返回模块入口页:
|
||||||
|
|
||||||
- `Makelore Code|AI 编程`:管理本地项目、项目智能体、对话、文件上下文、代码变更和运行时。
|
- `Makelore Code|AI 编程`:管理本地项目、项目智能体、对话、文件上下文、代码变更和运行时。
|
||||||
- `Makelore Canvas|AI 绘画`:每个设计项目(Workspace)维护一份从创建起就存在的 Living Form。用户通过对话或直接编辑持续完善同一设计方向,再生成图片、单参考图作品或视频;参考素材可从当前项目作品选择或从本地上传。Canvas 侧栏提供“获取灵感”,进入服务端驱动的提示词博物馆。
|
- `Makelore Canvas|AI 绘画`:每个设计项目(Workspace)维护一份从创建起就存在的 Living Form。用户通过对话或直接编辑持续完善同一设计方向,再生成图片、单参考图作品或视频;参考素材可从当前项目作品选择或从本地上传。
|
||||||
- `Makelore Robot|AI 机器`:管理机器人智能体、设备激活绑定、智能体配置与设备分配;机器人工作台的智能体位于 Robot 全局侧栏,选中后在内容区先查看绑定设备、再查看基础设置,当前智能体通过 URL 参数保持可分享选择;绑定设备时默认先选择“引导配网”或“已有激活码”。在 Windows 与 macOS 的引导路径中,Makelore 可在弹窗内扫描并连接附近开放的 `Xiaozhi-*` 配网热点,失败时仍可通过系统 Wi-Fi 手动连接;后续继续复用机器人现有热点配网页面,不修改固件,也不由 Makelore 接收 Wi-Fi 密码。
|
- `Makelore Robot|AI 机器`:管理机器人智能体、设备激活绑定、智能体配置与设备分配;机器人工作台的智能体位于 Robot 全局侧栏,选中后在内容区先查看绑定设备、再查看基础设置,当前智能体通过 URL 参数保持可分享选择;绑定设备时默认先选择“引导配网”或“已有激活码”。在 Windows 与 macOS 的引导路径中,Makelore 可在弹窗内扫描并连接附近开放的 `Xiaozhi-*` 配网热点,失败时仍可通过系统 Wi-Fi 手动连接;后续继续复用机器人现有热点配网页面,不修改固件,也不由 Makelore 接收 Wi-Fi 密码。
|
||||||
|
|
||||||
应用启动默认进入 AI 模块入口选择页。入口页可在未登录状态浏览;未登录用户点击已开通模块时进入客户端原生登录页,可使用账号密码或手机号短信验证码登录。密码登录可选“记住密码”:正式安装包仅由 Electron Main 使用系统受保护凭据存储加密保存和回填账号密码,不写入 Renderer 持久状态,未打包开发版或系统安全存储不可用时禁用该选项。登录请求由 Renderer 经 Host API 交给 Electron Main,再由 Main 调用 Works Square;成功后回到入口选择页。已登录时,Electron Main 会从 Works Square `/api/auth/me` 读取当前账号,只向 Renderer 投影用户名、账号/租户/部门标识、权限名列表与三个模块布尔开关,不透传上游资料或凭据。工作区门禁同时要求有效 Token 和完整用户身份;旧状态缺失身份时会先尝试从 Main 恢复,仍无法确认则清除残留会话并返回登录页。被管理员关闭的模块会在入口页置灰且无法点击,直接访问其工作区路径也会返回入口页。旧服务端未返回策略或缺少单项字段时默认开放;这个客户端门禁不替代服务端 API 授权。
|
应用启动默认进入 AI 模块入口选择页。入口页可在未登录状态浏览;未登录用户点击已开通模块时进入客户端原生登录页,可使用账号密码或手机号短信验证码登录。密码登录可选“记住密码”:正式安装包仅由 Electron Main 使用系统受保护凭据存储加密保存和回填账号密码,不写入 Renderer 持久状态,未打包开发版或系统安全存储不可用时禁用该选项。登录请求由 Renderer 经 Host API 交给 Electron Main,再由 Main 调用 Works Square;成功后回到入口选择页。已登录时,Electron Main 会从 Works Square `/api/auth/me` 读取当前账号,只向 Renderer 投影用户名、账号/租户/部门标识、权限名列表与三个模块布尔开关,不透传上游资料或凭据。工作区门禁同时要求有效 Token 和完整用户身份;旧状态缺失身份时会先尝试从 Main 恢复,仍无法确认则清除残留会话并返回登录页。被管理员关闭的模块会在入口页置灰且无法点击,直接访问其工作区路径也会返回入口页。旧服务端未返回策略或缺少单项字段时默认开放;这个客户端门禁不替代服务端 API 授权。
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ V1 不做:
|
|||||||
- 在客户端打包静态 Prompt 数据集;
|
- 在客户端打包静态 Prompt 数据集;
|
||||||
- 服务端代替用户自动发送 Prompt 或直接生成作品。
|
- 服务端代替用户自动发送 Prompt 或直接生成作品。
|
||||||
|
|
||||||
客户端入口位于 Canvas 侧栏“新建设计项目”上方,按钮文案为“获取灵感”。
|
当前客户端不再提供 Prompt Museum 的可见入口;历史 `/image-prompts` 地址会重定向到 Canvas 主页面。以下合同仅记录仍由 Main 持有的服务端边界。
|
||||||
|
|
||||||
## 2. 请求链路与接口
|
## 2. 请求链路与接口
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -36,7 +36,6 @@ const ProjectConfiguration = lazy(() => import('./pages/ProjectConfiguration').t
|
|||||||
const Plugins = lazy(() => import('./pages/Plugins').then(({ Plugins: component }) => ({ default: component })));
|
const Plugins = lazy(() => import('./pages/Plugins').then(({ Plugins: component }) => ({ default: component })));
|
||||||
const Workbench = lazy(() => import('./pages/Workbench').then(({ Workbench: component }) => ({ default: component })));
|
const Workbench = lazy(() => import('./pages/Workbench').then(({ Workbench: component }) => ({ default: component })));
|
||||||
const ImageCanvas = lazy(() => import('./pages/ImageCanvas').then(({ ImageCanvas: component }) => ({ default: component })));
|
const ImageCanvas = lazy(() => import('./pages/ImageCanvas').then(({ ImageCanvas: component }) => ({ default: component })));
|
||||||
const ImagePromptMuseum = lazy(() => import('./pages/ImagePromptMuseum').then(({ ImagePromptMuseum: component }) => ({ default: component })));
|
|
||||||
const AiHardware = lazy(() => import('./pages/AiHardware').then(({ AiHardware: component }) => ({ default: component })));
|
const AiHardware = lazy(() => import('./pages/AiHardware').then(({ AiHardware: component }) => ({ default: component })));
|
||||||
const Settings = lazy(() => import('./pages/Settings').then(({ Settings: component }) => ({ default: component })));
|
const Settings = lazy(() => import('./pages/Settings').then(({ Settings: component }) => ({ default: component })));
|
||||||
const Setup = lazy(() => import('./pages/Setup').then(({ Setup: component }) => ({ default: component })));
|
const Setup = lazy(() => import('./pages/Setup').then(({ Setup: component }) => ({ default: component })));
|
||||||
@@ -452,7 +451,7 @@ function App() {
|
|||||||
<Route path="/chat" element={<ProjectChatRoute />} />
|
<Route path="/chat" element={<ProjectChatRoute />} />
|
||||||
<Route path="/deliverables" element={<PreviewScene />} />
|
<Route path="/deliverables" element={<PreviewScene />} />
|
||||||
<Route path="/image-canvas" element={<ImageCanvas />} />
|
<Route path="/image-canvas" element={<ImageCanvas />} />
|
||||||
<Route path="/image-prompts" element={<ImagePromptMuseum />} />
|
<Route path="/image-prompts/*" element={<Navigate to="/image-canvas" replace />} />
|
||||||
<Route path="/ai-hardware" element={<AiHardware />} />
|
<Route path="/ai-hardware" element={<AiHardware />} />
|
||||||
<Route path="/workbench/:projectId" element={<Workbench />} />
|
<Route path="/workbench/:projectId" element={<Workbench />} />
|
||||||
<Route path="/models" element={<Models />} />
|
<Route path="/models" element={<Models />} />
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
FolderKanban,
|
FolderKanban,
|
||||||
Lightbulb,
|
|
||||||
Loader2,
|
Loader2,
|
||||||
Pencil,
|
Pencil,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -157,15 +156,6 @@ export function ImageWorkspaceSidebar({ sidebarCollapsed }: ImageWorkspaceSideba
|
|||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
size="icon"
|
|
||||||
aria-label="获取设计灵感"
|
|
||||||
onClick={() => navigate('/image-prompts')}
|
|
||||||
>
|
|
||||||
<Lightbulb className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -181,14 +171,6 @@ export function ImageWorkspaceSidebar({ sidebarCollapsed }: ImageWorkspaceSideba
|
|||||||
<Plus className="h-4 w-4 text-brand" />
|
<Plus className="h-4 w-4 text-brand" />
|
||||||
新建设计项目
|
新建设计项目
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="motion-press flex min-h-9 w-full items-center gap-2 rounded-xl px-2.5 text-left text-sm text-muted-foreground transition-colors hover:bg-surface-subtle hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/30"
|
|
||||||
onClick={() => navigate('/image-prompts')}
|
|
||||||
>
|
|
||||||
<Lightbulb className="h-4 w-4" />
|
|
||||||
获取灵感
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-2 mt-5 flex items-center justify-between px-2">
|
<div className="mb-2 mt-5 flex items-center justify-between px-2">
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ export function MainLayout() {
|
|||||||
const activeModule = getAiModuleForPath(location.pathname);
|
const activeModule = getAiModuleForPath(location.pathname);
|
||||||
const isProgrammingModule = activeModule === 'programming';
|
const isProgrammingModule = activeModule === 'programming';
|
||||||
const isPaintingModule = activeModule === 'painting';
|
const isPaintingModule = activeModule === 'painting';
|
||||||
const isPromptMuseum = location.pathname === '/image-prompts' || location.pathname.startsWith('/image-prompts/');
|
|
||||||
const isChatWorkspace = location.pathname === '/chat';
|
const isChatWorkspace = location.pathname === '/chat';
|
||||||
const isInitializationSafeRoute = location.pathname === '/project-config'
|
const isInitializationSafeRoute = location.pathname === '/project-config'
|
||||||
|| location.pathname === '/plugins'
|
|| location.pathname === '/plugins'
|
||||||
@@ -95,8 +94,7 @@ export function MainLayout() {
|
|||||||
<TitleBar
|
<TitleBar
|
||||||
integrated
|
integrated
|
||||||
workspaceLayout={isChatWorkspace}
|
workspaceLayout={isChatWorkspace}
|
||||||
overlay={isPaintingModule && !isPromptMuseum}
|
overlay={isPaintingModule}
|
||||||
pageTitle={isPromptMuseum ? '获取灵感' : undefined}
|
|
||||||
sidebarPeekOpen={sidebarPeekOpen}
|
sidebarPeekOpen={sidebarPeekOpen}
|
||||||
onSidebarPeekChange={handleSidebarPeekChange}
|
onSidebarPeekChange={handleSidebarPeekChange}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -185,7 +185,6 @@ export function Sidebar({ workspaceLayout = false, sidebarPeekOpen = false, onSi
|
|||||||
const isProgrammingModule = activeModule === 'programming';
|
const isProgrammingModule = activeModule === 'programming';
|
||||||
const isPaintingModule = activeModule === 'painting';
|
const isPaintingModule = activeModule === 'painting';
|
||||||
const isRobotModule = activeModule === 'robot';
|
const isRobotModule = activeModule === 'robot';
|
||||||
const isPromptMuseum = location.pathname === '/image-prompts' || location.pathname.startsWith('/image-prompts/');
|
|
||||||
const projectConfigPath = '/project-config';
|
const projectConfigPath = '/project-config';
|
||||||
const visibleProjects = projects;
|
const visibleProjects = projects;
|
||||||
const selectedProjectFolderName = getFolderName(newProjectSelectedPath);
|
const selectedProjectFolderName = getFolderName(newProjectSelectedPath);
|
||||||
@@ -557,7 +556,7 @@ export function Sidebar({ workspaceLayout = false, sidebarPeekOpen = false, onSi
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-transparent px-3 text-foreground',
|
'bg-transparent px-3 text-foreground',
|
||||||
isPaintingModule && !isPromptMuseum && !sidebarCollapsed ? 'pb-2 pt-10' : 'py-2',
|
isPaintingModule && !sidebarCollapsed ? 'pb-2 pt-10' : 'py-2',
|
||||||
sidebarCollapsed && 'px-2',
|
sidebarCollapsed && 'px-2',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
|
import { closeElectronApp, expect, getStableWindow, test } from './fixtures/electron';
|
||||||
|
|
||||||
test.describe('AI Design V2 workspace', () => {
|
test.describe('AI Design V2 workspace', () => {
|
||||||
test('keeps Prompt Museum cards usable when relative media fails', async ({ launchElectronApp }) => {
|
test('removes the inspiration entry and redirects its retired route to Canvas', async ({ launchElectronApp }) => {
|
||||||
test.setTimeout(90_000);
|
test.setTimeout(90_000);
|
||||||
const app = await launchElectronApp({ skipSetup: true });
|
const app = await launchElectronApp({ skipSetup: true });
|
||||||
|
|
||||||
@@ -10,29 +10,6 @@ test.describe('AI Design V2 workspace', () => {
|
|||||||
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
|
await expect(page.getByTestId('ai-module-selection-page')).toBeVisible();
|
||||||
|
|
||||||
await app.evaluate(({ ipcMain }) => {
|
await app.evaluate(({ ipcMain }) => {
|
||||||
const image = {
|
|
||||||
url: '/api/image-prompt-museum/e2e-entry/media/thumbnail',
|
|
||||||
width: 1200,
|
|
||||||
height: 900,
|
|
||||||
alt: 'E2E museum thumbnail',
|
|
||||||
};
|
|
||||||
const card = {
|
|
||||||
id: 'e2e-entry',
|
|
||||||
slug: 'e2e-entry',
|
|
||||||
title: '相对媒体 E2E',
|
|
||||||
summary: '验证图片失败不会阻断卡片。',
|
|
||||||
thumbnail: image,
|
|
||||||
categories: [],
|
|
||||||
model: { id: 'e2e-model', name: 'E2E Model' },
|
|
||||||
language: 'zh-CN',
|
|
||||||
attribution: {
|
|
||||||
author: { name: 'E2E 作者' },
|
|
||||||
source: { name: 'E2E 来源' },
|
|
||||||
license: { name: '测试许可', attributionText: 'E2E attribution' },
|
|
||||||
},
|
|
||||||
publishedAt: '2026-08-18T00:00:00.000Z',
|
|
||||||
updatedAt: '2026-08-18T00:00:00.000Z',
|
|
||||||
};
|
|
||||||
const respond = (json: unknown) => ({
|
const respond = (json: unknown) => ({
|
||||||
ok: true,
|
ok: true,
|
||||||
data: { status: 200, ok: true, json },
|
data: { status: 200, ok: true, json },
|
||||||
@@ -50,47 +27,20 @@ test.describe('AI Design V2 workspace', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (path === '/api/works/image-prompt-museum') {
|
|
||||||
return respond({
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
items: [card],
|
|
||||||
facets: { useCases: [], styles: [], subjects: [] },
|
|
||||||
nextCursor: null,
|
|
||||||
total: 1,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (path === '/api/works/image-prompt-museum/e2e-entry') {
|
|
||||||
return respond({
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
...card,
|
|
||||||
prompt: 'Create a resilient image card.',
|
|
||||||
variables: [],
|
|
||||||
images: [image],
|
|
||||||
requiresReferenceImages: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (path === '/api/works/image-prompt-museum/e2e-entry/media/thumbnail') {
|
|
||||||
return respond({ mimeType: 'text/html', dataBase64: 'PGgxPnVuc2FmZTwvaDE+' });
|
|
||||||
}
|
|
||||||
return { ok: false, error: { message: `Unexpected E2E Host API request: ${path}` } };
|
return { ok: false, error: { message: `Unexpected E2E Host API request: ${path}` } };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByTestId('ai-module-option-painting').click();
|
await page.getByTestId('ai-module-option-painting').click();
|
||||||
const imageSidebar = page.getByTestId('sidebar-image-workspace');
|
const imageSidebar = page.getByTestId('sidebar-image-workspace');
|
||||||
await expect(imageSidebar.getByRole('button', { name: '获取灵感' })).toBeVisible();
|
await expect(imageSidebar).toBeVisible();
|
||||||
await imageSidebar.getByRole('button', { name: '获取灵感' }).click();
|
await expect(imageSidebar.getByRole('button', { name: /获取.*灵感/u })).toHaveCount(0);
|
||||||
|
|
||||||
const cardButton = page.getByRole('button', { name: '查看 相对媒体 E2E' });
|
await page.evaluate(() => {
|
||||||
await expect(cardButton).toBeVisible();
|
window.location.hash = '#/image-prompts';
|
||||||
await expect(page.getByLabel('E2E museum thumbnail加载失败')).toBeVisible();
|
});
|
||||||
await expect(cardButton).toBeEnabled();
|
await expect.poll(() => page.evaluate(() => window.location.hash)).toBe('#/image-canvas');
|
||||||
await cardButton.click();
|
await expect(page.getByTestId('sidebar-image-workspace')).toBeVisible();
|
||||||
await expect(page.getByRole('button', { name: '关闭详情' })).toBeVisible();
|
|
||||||
} finally {
|
} finally {
|
||||||
await closeElectronApp(app);
|
await closeElectronApp(app);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,14 @@ describe('App programming provider initialization gate', () => {
|
|||||||
await waitFor(() => expect(initProviders).toHaveBeenCalledTimes(1));
|
await waitFor(() => expect(initProviders).toHaveBeenCalledTimes(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('redirects the retired inspiration route to the Canvas workspace', async () => {
|
||||||
|
await renderAt('/image-prompts/example');
|
||||||
|
|
||||||
|
expect(await screen.findByText('Canvas workspace')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByText('Module chooser')).not.toBeInTheDocument();
|
||||||
|
expect(initProviders).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('redirects an orphaned token without a user identity away from a direct workspace route', async () => {
|
it('redirects an orphaned token without a user identity away from a direct workspace route', async () => {
|
||||||
useAuthStore.setState({
|
useAuthStore.setState({
|
||||||
initialized: true,
|
initialized: true,
|
||||||
|
|||||||
@@ -78,16 +78,6 @@ describe('MainLayout module isolation', () => {
|
|||||||
expect(screen.getByTestId('titlebar-stub')).toHaveAttribute('data-overlay', 'true');
|
expect(screen.getByTestId('titlebar-stub')).toHaveAttribute('data-overlay', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps the user sidebar preference while moving the inspiration title into the title bar', () => {
|
|
||||||
renderLayout('/image-prompts');
|
|
||||||
|
|
||||||
expect(screen.getByTestId('titlebar-stub')).toHaveAttribute('data-page-title', '获取灵感');
|
|
||||||
expect(screen.getByTestId('titlebar-stub')).toHaveAttribute('data-overlay', 'false');
|
|
||||||
expect(screen.getByTestId('titlebar-stub')).toHaveAttribute('data-sidebar-collapsed', 'unset');
|
|
||||||
expect(screen.getByTestId('sidebar-stub')).toHaveAttribute('data-sidebar-collapsed', 'unset');
|
|
||||||
expect(useSettingsStore.getState().sidebarCollapsed).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not overlay the local project initialization gate on AI hardware', () => {
|
it('does not overlay the local project initialization gate on AI hardware', () => {
|
||||||
const load = vi.fn().mockResolvedValue(undefined);
|
const load = vi.fn().mockResolvedValue(undefined);
|
||||||
useProjectConfigStore.setState({ load });
|
useProjectConfigStore.setState({ load });
|
||||||
|
|||||||
@@ -121,13 +121,13 @@ describe('TitleBar platform behavior', () => {
|
|||||||
expect(screen.queryByTestId('titlebar-project-context')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('titlebar-project-context')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps the painting page title after the macOS traffic-light safe area', () => {
|
it('keeps an optional page title after the macOS traffic-light safe area', () => {
|
||||||
window.electron.platform = 'darwin';
|
window.electron.platform = 'darwin';
|
||||||
useSettingsStore.setState({ sidebarCollapsed: true });
|
useSettingsStore.setState({ sidebarCollapsed: true });
|
||||||
|
|
||||||
render(<TitleBar integrated pageTitle="获取灵感" />);
|
render(<TitleBar integrated pageTitle="页面标题" />);
|
||||||
|
|
||||||
expect(screen.getByTestId('titlebar-page-title')).toHaveTextContent('获取灵感');
|
expect(screen.getByTestId('titlebar-page-title')).toHaveTextContent('页面标题');
|
||||||
expect(screen.getByTestId('titlebar-sidebar-surface')).toHaveClass('w-[132px]');
|
expect(screen.getByTestId('titlebar-sidebar-surface')).toHaveClass('w-[132px]');
|
||||||
expect(screen.queryByTestId('titlebar-project-context')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('titlebar-project-context')).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('titlebar-logo').querySelectorAll('img')).toHaveLength(1);
|
expect(screen.getByTestId('titlebar-logo').querySelectorAll('img')).toHaveLength(1);
|
||||||
|
|||||||
Reference in new issue
Block a user