Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8ce1d9031 | ||
|
|
edbefcc738 |
+1
-1
@@ -3,7 +3,7 @@
|
||||
"browsers": [
|
||||
{
|
||||
"brand": "Google Chrome",
|
||||
"fullVersion": "150.0.7871.187"
|
||||
"fullVersion": "151.0.0.0"
|
||||
},
|
||||
{
|
||||
"brand": "Microsoft Edge",
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
:root {
|
||||
--dada-interaction-duration: 120ms;
|
||||
--dada-interaction-easing: cubic-bezier(0.2, 0.8, 0.2, 1);
|
||||
}
|
||||
|
||||
:where(button:not(:disabled), a[href], label:has(input:not(:disabled))) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:where(button:not(:disabled), a[href]) {
|
||||
transition:
|
||||
transform var(--dada-interaction-duration) var(--dada-interaction-easing),
|
||||
box-shadow var(--dada-interaction-duration) var(--dada-interaction-easing),
|
||||
border-color var(--dada-interaction-duration) ease,
|
||||
background-color var(--dada-interaction-duration) ease,
|
||||
color var(--dada-interaction-duration) ease,
|
||||
opacity var(--dada-interaction-duration) ease;
|
||||
}
|
||||
|
||||
:where(input:not(:disabled), select:not(:disabled), textarea:not(:disabled)) {
|
||||
transition:
|
||||
border-color var(--dada-interaction-duration) ease,
|
||||
box-shadow var(--dada-interaction-duration) ease,
|
||||
background-color var(--dada-interaction-duration) ease;
|
||||
}
|
||||
|
||||
:where(.product-page, .product-loading) :focus-visible {
|
||||
outline: 2px solid #005fcc;
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
:where(input:not(:disabled), select:not(:disabled), textarea:not(:disabled)):focus-visible {
|
||||
border-color: #005fcc;
|
||||
box-shadow: 0 0 0 3px rgb(0 95 204 / 16%);
|
||||
}
|
||||
|
||||
.project-card,
|
||||
.project-preview img,
|
||||
.ratio-control span,
|
||||
.reference-input,
|
||||
.editor-sticker-preview,
|
||||
.editor-template-mark,
|
||||
.editor-color-card-preview,
|
||||
.editor-dynamic-preview,
|
||||
.editor-source-preview-canvas,
|
||||
.editor-thumb {
|
||||
transition:
|
||||
transform var(--dada-interaction-duration) var(--dada-interaction-easing),
|
||||
box-shadow var(--dada-interaction-duration) var(--dada-interaction-easing),
|
||||
border-color var(--dada-interaction-duration) ease,
|
||||
background-color var(--dada-interaction-duration) ease;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
:where(button:not(:disabled)):hover {
|
||||
border-color: #111111;
|
||||
box-shadow: 0 2px 0 rgb(17 17 17 / 35%);
|
||||
}
|
||||
|
||||
:where(a[href]):hover {
|
||||
color: #005fcc;
|
||||
opacity: 0.78;
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 3px;
|
||||
}
|
||||
|
||||
.product-header nav a:hover {
|
||||
color: #111111;
|
||||
background: #e9e9e5;
|
||||
box-shadow: inset 0 -3px #111111;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.product-header nav a[aria-current="page"]:hover {
|
||||
background: #f2f500;
|
||||
}
|
||||
|
||||
.ratio-control label:hover span,
|
||||
.reference-input:hover {
|
||||
border-color: #111111;
|
||||
background: #ffffd6;
|
||||
box-shadow: inset 0 -3px #111111;
|
||||
}
|
||||
|
||||
.project-card:hover {
|
||||
border-color: #111111;
|
||||
box-shadow: 0 3px 0 rgb(17 17 17 / 22%);
|
||||
}
|
||||
|
||||
.editor-asset-tabs button:not(:disabled):hover,
|
||||
.editor-source:hover,
|
||||
.editor-sticker-grid button:not(:disabled):hover,
|
||||
.editor-template-categories button:not(:disabled):hover,
|
||||
.editor-template-grid button:not(:disabled):hover,
|
||||
.editor-provider-grid button:not(:disabled):hover,
|
||||
.editor-candidates button:not(:disabled):hover {
|
||||
border-color: #111111;
|
||||
background: #ffffd6;
|
||||
}
|
||||
|
||||
:where(input:not(:disabled), select:not(:disabled), textarea:not(:disabled)):hover {
|
||||
border-color: #111111;
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
|
||||
:where(button:not(:disabled), a[href]):hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.project-card:hover .project-preview img,
|
||||
.editor-sticker-grid button:not(:disabled):hover .editor-sticker-preview {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.editor-template-grid button:not(:disabled):hover .editor-template-mark,
|
||||
.editor-provider-grid button:not(:disabled):hover > :first-child {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
:where(button:not(:disabled), a[href]):active {
|
||||
transform: translateY(1px);
|
||||
transition-duration: 45ms;
|
||||
}
|
||||
}
|
||||
|
||||
:where(button:not(:disabled)):active {
|
||||
box-shadow: inset 0 2px 0 rgb(17 17 17 / 24%);
|
||||
}
|
||||
|
||||
:where(a[href]):active {
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:where(button, a[href], input, select, textarea),
|
||||
.project-card,
|
||||
.project-preview img,
|
||||
.ratio-control span,
|
||||
.reference-input,
|
||||
.editor-sticker-preview,
|
||||
.editor-template-mark,
|
||||
.editor-color-card-preview,
|
||||
.editor-dynamic-preview,
|
||||
.editor-source-preview-canvas,
|
||||
.editor-thumb {
|
||||
animation-duration: 0s !important;
|
||||
transition-duration: 0s !important;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@ import { ProjectDetailPage, ProjectsPage, WorkspacePage } from "./project-pages.
|
||||
import { EditorPage } from "./editor-page.js";
|
||||
import { AdminOverviewPage, AdminPlaceholderPage, AdminProtectedRoute } from "./admin-shell.js";
|
||||
|
||||
import "./interaction-feedback.css";
|
||||
|
||||
const root = document.getElementById("root");
|
||||
|
||||
if (!root) {
|
||||
|
||||
@@ -27,6 +27,7 @@ interface LocalDataPayload {
|
||||
}
|
||||
|
||||
interface AccountSettingsPayload {
|
||||
csrf_token: string;
|
||||
local_data: LocalDataPayload;
|
||||
}
|
||||
|
||||
@@ -296,7 +297,12 @@ export function WorkspacePage() {
|
||||
if (!active) return;
|
||||
if (modelResult.status === "fulfilled") setModels(modelResult.value);
|
||||
if (taskResult.status === "fulfilled") setCurrentTask(taskResult.value);
|
||||
if (settingsResult.status === "fulfilled" && settingsResult.value) setLocalData(settingsResult.value.local_data);
|
||||
const settings = settingsResult.status === "fulfilled" ? settingsResult.value : undefined;
|
||||
if (settings) {
|
||||
setLocalData(settings.local_data);
|
||||
// Account settings rotates the mutation token; keep the workspace token current.
|
||||
setSession((current) => current ? { ...current, csrf_token: settings.csrf_token } : current);
|
||||
}
|
||||
setGenerationStateLoaded(true);
|
||||
});
|
||||
}).catch((error) => {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("POSTV1-11 workspace generation CSRF refresh", () => {
|
||||
it("keeps the rotated account-settings token for generation submission", () => {
|
||||
const source = readFileSync("apps/web/src/project-pages.tsx", "utf8");
|
||||
|
||||
expect(source).toContain("csrf_token: string;");
|
||||
expect(source).toContain("csrf_token: settings.csrf_token");
|
||||
});
|
||||
});
|
||||
@@ -105,9 +105,21 @@ test("TDD-WP2-PROJ-005 limits failed-empty selection on the project list", async
|
||||
await expect(page.getByLabel("选择失败草稿:失败草稿")).toBeVisible();
|
||||
await expect(page.getByLabel("选择失败草稿:城市工作室")).toHaveCount(0);
|
||||
await page.getByLabel("选择失败草稿:失败草稿").check();
|
||||
await page.getByRole("button", { name: "批量移入回收站" }).click();
|
||||
const batchTrashButton = page.getByRole("button", { name: "批量移入回收站" });
|
||||
await batchTrashButton.hover();
|
||||
await expect(batchTrashButton).toHaveCSS("transform", "matrix(1, 0, 0, 1, 0, -1)");
|
||||
const buttonBounds = await batchTrashButton.boundingBox();
|
||||
if (!buttonBounds) throw new Error("Batch trash button geometry is unavailable.");
|
||||
await page.mouse.move(buttonBounds.x + buttonBounds.width / 2, buttonBounds.y + buttonBounds.height / 2);
|
||||
await page.mouse.down();
|
||||
await expect(batchTrashButton).toHaveCSS("transform", "matrix(1, 0, 0, 1, 0, 1)");
|
||||
await page.mouse.move(0, 0);
|
||||
await page.mouse.up();
|
||||
await batchTrashButton.click();
|
||||
expect(batchPayload).toEqual({ project_ids: [failedId] });
|
||||
await expect(page.getByText("失败草稿已移入回收站")).toBeVisible();
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await expect(page.getByRole("link", { name: "打开项目:城市工作室" })).toHaveCSS("transition-duration", "0s");
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(375);
|
||||
await captureEvidence(page, "TDD-WP2-PROJ-005-failed-draft-retry", "projects-mobile.png");
|
||||
});
|
||||
|
||||
@@ -98,7 +98,11 @@ test("TDD-WP4-TXT-003 exposes the frozen catalog, display-name search and accoun
|
||||
await expect(page.locator(".editor-template-grid button")).toHaveCount(0);
|
||||
await search.fill("");
|
||||
expect(page.getByText("添加普通文字", { exact: true })).toHaveCount(0);
|
||||
await page.getByRole("button", { name: /FLOWER001 春日计划/ }).click();
|
||||
const flowerTemplate = page.getByRole("button", { name: /FLOWER001 春日计划/ });
|
||||
await flowerTemplate.hover();
|
||||
await expect(flowerTemplate).toHaveCSS("background-color", "rgb(255, 255, 214)");
|
||||
await expect(flowerTemplate.locator(".editor-template-mark")).toHaveCSS("transform", "matrix(1.03, 0, 0, 1.03, 0, 0)");
|
||||
await flowerTemplate.click();
|
||||
await expect(page.getByText("对象 1 / 50")).toBeVisible();
|
||||
await expect.poll(() => backend.recent).toEqual([{ asset_id: "FLOWER001", asset_kind: "text_template", resource_version: "p0a-complex-v1" }]);
|
||||
await page.reload();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
test("POSTV1-11 release accepts the Codex embedded Chrome major", () => {
|
||||
const release = JSON.parse(
|
||||
fs.readFileSync(path.resolve("RELEASE.json"), "utf8"),
|
||||
);
|
||||
const chrome = release.browsers.find(({ brand }) => brand === "Google Chrome");
|
||||
|
||||
assert.ok(chrome, "release must include Google Chrome");
|
||||
assert.equal(Number.parseInt(chrome.fullVersion.split(".")[0], 10), 151);
|
||||
});
|
||||
Reference in New Issue
Block a user