fix: complete approved layout and prompt cancellation safeguards
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -26,6 +26,7 @@
|
||||
min-width: 0;
|
||||
}
|
||||
.mdl-sum {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
@@ -55,8 +56,10 @@
|
||||
color: var(--tx-2);
|
||||
font-size: 11.5px;
|
||||
margin-top: 2px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.mlist {
|
||||
flex: none;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--bg-1);
|
||||
@@ -189,6 +192,7 @@
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
html[data-motion="calm"] .spill.run i,
|
||||
html[data-motion="off"] .spill.run i {
|
||||
animation: none;
|
||||
}
|
||||
@@ -214,6 +218,7 @@ html[data-motion="off"] .spill.run i {
|
||||
border-color: color-mix(in srgb, var(--sc-image) 40%, transparent);
|
||||
}
|
||||
.role-t {
|
||||
flex: none;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--bg-1);
|
||||
@@ -257,6 +262,7 @@ html[data-motion="off"] .spill.run i {
|
||||
height: 28px;
|
||||
}
|
||||
.soon-row {
|
||||
flex: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
@@ -399,11 +405,7 @@ html[data-motion="off"] .spill.run i {
|
||||
@media (max-width: 1280px) {
|
||||
.mlist-h,
|
||||
.mrow {
|
||||
grid-template-columns: minmax(0, 1.3fr) 150px 84px;
|
||||
}
|
||||
.mlist-h .c-use,
|
||||
.mrow .use {
|
||||
display: none;
|
||||
grid-template-columns: minmax(0, 1fr) 150px 84px;
|
||||
}
|
||||
.role-g {
|
||||
grid-template-columns: 1fr;
|
||||
@@ -414,6 +416,10 @@ html[data-motion="off"] .spill.run i {
|
||||
.role:nth-last-child(2) {
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.mlist-h .use,
|
||||
.mrow .use {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mdl-body small,
|
||||
@@ -457,21 +463,8 @@ html[data-motion="off"] .spill.run i {
|
||||
width: 100%;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
@media (max-width: 1280px) {
|
||||
.mlist-h,
|
||||
.mrow {
|
||||
grid-template-columns: minmax(0, 1fr) 150px 84px;
|
||||
}
|
||||
.mlist-h .use,
|
||||
.mrow .use {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.role > span {
|
||||
font-size: 12px;
|
||||
color: var(--tx-2);
|
||||
}
|
||||
.mdl-alert small {
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import ToolButtons from "./ToolButtons.svelte";
|
||||
import ResultBand from "./ResultBand.svelte";
|
||||
let {
|
||||
value = $bindable(""),
|
||||
value = $bindable(),
|
||||
identity,
|
||||
label = "提示词",
|
||||
rows = 2,
|
||||
@@ -18,7 +18,7 @@
|
||||
placeholder?: string;
|
||||
} = $props();
|
||||
const tool = promptTool(
|
||||
() => value,
|
||||
() => value ?? "",
|
||||
() => `${page.url.pathname}|${identity}`,
|
||||
(v) => (value = v),
|
||||
untrack(() => label),
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
|
||||
<svelte:window
|
||||
onkeydown={(e) => {
|
||||
if (e.key === "Escape" && tool.state !== "idle") tool.close();
|
||||
if (e.key === "Escape" && tool.state !== "idle") {
|
||||
if (tool.state === "loading") tool.cancel();
|
||||
else tool.close();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{#if tool.state !== "idle"}
|
||||
@@ -36,7 +39,7 @@
|
||||
{#if tool.state === "loading"}<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onclick={tool.close}>取消</Button
|
||||
onclick={tool.cancel}>取消</Button
|
||||
>
|
||||
{:else if tool.state === "result"}
|
||||
{#if tool.confirm}<span class="pt-confirm">替换会覆盖你刚才的修改</span
|
||||
@@ -68,7 +71,8 @@
|
||||
type="button"
|
||||
class="pt-btn"
|
||||
aria-label="关闭提示词结果"
|
||||
onclick={tool.close}>×</button
|
||||
onclick={() =>
|
||||
tool.state === "loading" ? tool.cancel() : tool.close()}>×</button
|
||||
>
|
||||
</div>
|
||||
<div class="pt-b">
|
||||
|
||||
@@ -55,6 +55,10 @@ export function promptTool(
|
||||
state = "idle";
|
||||
confirm = false;
|
||||
}
|
||||
function cancel() {
|
||||
close();
|
||||
toast.info("已取消,原提示词未改动");
|
||||
}
|
||||
function edit() {
|
||||
++textVersion;
|
||||
confirm = false;
|
||||
@@ -63,9 +67,12 @@ export function promptTool(
|
||||
const context = getContext();
|
||||
const text = getText();
|
||||
if (context !== observedContext) {
|
||||
const hadResult = state !== "idle";
|
||||
observedContext = context;
|
||||
++contextVersion;
|
||||
close();
|
||||
if (hadResult)
|
||||
toast.info("已切换工作流或项目,旧结果已作废;提示词未改动");
|
||||
}
|
||||
if (text !== observedText) {
|
||||
observedText = text;
|
||||
@@ -87,8 +94,7 @@ export function promptTool(
|
||||
sync();
|
||||
if (!getText().trim()) return;
|
||||
if (state === "loading" && next === action) {
|
||||
close();
|
||||
toast.info("已取消,原提示词未改动");
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
onOpen?.();
|
||||
@@ -230,6 +236,7 @@ export function promptTool(
|
||||
},
|
||||
run,
|
||||
close,
|
||||
cancel,
|
||||
edit,
|
||||
replace,
|
||||
cancelConfirm() {
|
||||
|
||||
@@ -422,22 +422,24 @@
|
||||
/>{:else}<span class="cover-blank">暂无封面</span>{/if}
|
||||
</div>
|
||||
<div class="copy">
|
||||
<small
|
||||
<span class="eyebrow"
|
||||
>继续创作 · {new Date(
|
||||
parseDbTs(recent.updated_at),
|
||||
).toLocaleDateString()}</small
|
||||
).toLocaleDateString()}</span
|
||||
>
|
||||
<h2 class="t1" title={recent.title}>《{recent.title}》</h2>
|
||||
<b class="resume-stage"
|
||||
>{recentStage < 0
|
||||
? "已完成"
|
||||
: stageNames[recentStage] +
|
||||
" · " +
|
||||
(recent.stats?.stage_status?.[stageKeys[recentStage]] ===
|
||||
"awaiting_confirm"
|
||||
? "等你确认"
|
||||
: "继续创作")}</b
|
||||
>
|
||||
<h2 class="t2">
|
||||
<em
|
||||
>{recentStage < 0
|
||||
? "已完成"
|
||||
: stageNames[recentStage] +
|
||||
" · " +
|
||||
(recent.stats?.stage_status?.[stageKeys[recentStage]] ===
|
||||
"awaiting_confirm"
|
||||
? "等你确认"
|
||||
: "继续创作")}</em
|
||||
>
|
||||
</h2>
|
||||
<p class="next">草稿确认后,才会交给下一阶段使用。</p>
|
||||
<div class="studio-stages">
|
||||
{#each stageKeys as key, i (key)}<span
|
||||
|
||||
@@ -272,7 +272,8 @@
|
||||
</div>
|
||||
<div class="lgrid" class:dense class:fill>
|
||||
{#each group.items as i (i.path)}<article
|
||||
class="lt"
|
||||
class="lt a-viewfinder"
|
||||
class:a-in-frame={focused?.path === i.path}
|
||||
class:on={focused?.path === i.path}
|
||||
class:picked={selected.has(i.path)}
|
||||
class:broken={broken[i.path]}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.lib2 {
|
||||
--viewfinder-position-duration: var(--t-base);
|
||||
flex: 1;
|
||||
display: grid;
|
||||
grid-template-columns: var(--lib-filter) minmax(0, 1fr) var(--lib-detail);
|
||||
@@ -494,6 +495,8 @@
|
||||
}
|
||||
.lt .mat img,
|
||||
.lt .mat video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
@@ -503,6 +506,8 @@
|
||||
}
|
||||
.ld-pv img,
|
||||
.ld-pv video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
@@ -537,3 +542,17 @@
|
||||
height: 60px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
html[data-motion="full"] .selbar.on {
|
||||
animation: library-selection var(--t-base) var(--ease-out);
|
||||
}
|
||||
@keyframes library-selection {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, 12px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
.resume h2 {
|
||||
font-size: 22px;
|
||||
@@ -381,17 +381,11 @@
|
||||
font: 11px var(--mono);
|
||||
color: var(--tx-3);
|
||||
}
|
||||
.resume .copy > small {
|
||||
font-size: 11px;
|
||||
color: var(--tx-2);
|
||||
}
|
||||
.resume-stage {
|
||||
font-size: 14px;
|
||||
color: var(--gold);
|
||||
}
|
||||
.studio-stages {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
margin-top: 4px;
|
||||
max-width: 460px;
|
||||
}
|
||||
.studio-stages > span {
|
||||
display: flex;
|
||||
@@ -405,17 +399,21 @@
|
||||
.studio-stages i {
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
border-radius: 50%;
|
||||
background: var(--tx-3);
|
||||
border-radius: 3px;
|
||||
background: rgb(var(--ink) / 0.12);
|
||||
}
|
||||
.studio-stages .done i {
|
||||
background: var(--ok);
|
||||
background: var(--gold);
|
||||
}
|
||||
.studio-stages .cur {
|
||||
color: var(--gold);
|
||||
color: var(--tx-1);
|
||||
}
|
||||
.studio-stages .cur i {
|
||||
background: var(--gold);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--gold) 50%,
|
||||
rgb(var(--ink) / 0.12) 50%
|
||||
);
|
||||
}
|
||||
.start > span:last-child {
|
||||
min-width: 0;
|
||||
@@ -513,3 +511,22 @@
|
||||
.st2 :is(h2, h3, p) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.resume .eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
height: 22px;
|
||||
padding: 0 9px;
|
||||
border-radius: var(--r-pill);
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.5px;
|
||||
width: max-content;
|
||||
color: color-mix(in srgb, var(--scene) 75%, var(--hi));
|
||||
border: 1px solid color-mix(in srgb, var(--scene) 45%, transparent);
|
||||
background: color-mix(in srgb, var(--scene) 10%, transparent);
|
||||
}
|
||||
|
||||
.studio-stages .done {
|
||||
color: var(--tx-1);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ HEL-840:第二版布局与提示词工具
|
||||
|
||||
视觉与动效
|
||||
|
||||
采用原有颜色/字体/圆角/动效 token,新增批准的几何 token;共享页头样式抽到唯一来源。创作台外壳/舞台不改,结果条沿现有 grid 重排。小窗角标仅防换行和给工作流名省略,不改变外壳。双环源码未变。
|
||||
模型列表/分工按内容高度在主容器滚动,窄窗不收缩丢行;素材媒体固定在比例容器内,默认 contain 不裁切;复用现有取景框,并按批准档位控制选择条与测试状态动效。采用原有颜色/字体/圆角/动效 token,新增批准的几何 token;共享页头样式抽到唯一来源。创作台外壳/舞台不改,结果条沿现有 grid 重排。小窗角标仅防换行和给工作流名省略,不改变外壳。双环源码未变。
|
||||
|
||||
| 宽度 | 工作室右栏 | 素材筛选 / 详情 | 模型详情 |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
@@ -16,3 +16,5 @@ Additional verification:
|
||||
- `python3 scripts/hel840/compare_reference.py --reference /path/to/approved-v2.html --output ./evidence/hel840/tokens.json` compares token values; it does not claim screenshot pixel equality.
|
||||
|
||||
The approved original HTML and PNGs must be supplied unchanged from the approved attachment. Keep originals, mapping and actual application captures separate. For cancellation/timeout screenshots the isolated fixture accelerates the total timeout to 1.5 seconds; the product uses 120 seconds. Complete recognized weighted expressions are preserved verbatim so their weights cannot move to another operand. Closing a request does not promise that a supplier stops charging.
|
||||
|
||||
Review packaging supports `--matrix-run finalized --states-run finalized` when the final full capture was recorded under that directory. This keeps a single source capture for the delivered matrix and states.
|
||||
|
||||
@@ -118,6 +118,8 @@ try:
|
||||
1
|
||||
).click()
|
||||
page.wait_for_timeout(150)
|
||||
assert page.locator(".mlist").bounding_box()["height"] >= 212
|
||||
assert page.locator(".mrow").first.bounding_box()["y"] >= 44
|
||||
if label.startswith("04"):
|
||||
page.locator("#desk-prompt").wait_for()
|
||||
page.locator("#desk-prompt").fill(
|
||||
@@ -137,7 +139,7 @@ try:
|
||||
"height": height,
|
||||
"page": label,
|
||||
"dom": page.evaluate(
|
||||
"""()=>{const rect=s=>{const n=document.querySelector(s);if(!n)return null;const r=n.getBoundingClientRect(),c=getComputedStyle(n);return {x:r.x,y:r.y,width:r.width,height:r.height,bottom:r.bottom,font:c.font,fontFamily:c.fontFamily,color:c.color,background:c.backgroundColor}};return {overflow:document.documentElement.scrollWidth>innerWidth,theme:document.documentElement.dataset.theme,main:rect('main'),resume:rect('.resume'),library:rect('.lib2'),filter:rect('.mg-side'),detail:rect('.lib-detail'),models:rect('.mdl'),modelDetail:rect('.mdl-detail'),band:rect('.ptband'),dock:rect('.studio-form'),media:rect('.stage-media .print'),generate:rect('.gen')}}"""
|
||||
"""()=>{const rect=s=>{const n=document.querySelector(s);if(!n)return null;const r=n.getBoundingClientRect(),c=getComputedStyle(n);return {x:r.x,y:r.y,width:r.width,height:r.height,bottom:r.bottom,font:c.font,fontFamily:c.fontFamily,color:c.color,background:c.backgroundColor}};return {overflow:document.documentElement.scrollWidth>innerWidth,theme:document.documentElement.dataset.theme,main:rect('main'),resume:rect('.resume'),library:rect('.lib2'),filter:rect('.mg-side'),detail:rect('.lib-detail'),models:rect('.mdl'),modelList:rect('.mlist'),modelRow:rect('.mrow'),modelDetail:rect('.mdl-detail'),band:rect('.ptband'),dock:rect('.studio-form'),media:rect('.stage-media .print'),generate:rect('.gen')}}"""
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -12,6 +12,8 @@ parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--approved", type=Path, required=True)
|
||||
parser.add_argument("--evidence", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
parser.add_argument("--matrix-run", default="delivery")
|
||||
parser.add_argument("--states-run", default="default-verification")
|
||||
args = parser.parse_args()
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
evidence = args.evidence.resolve()
|
||||
@@ -36,19 +38,19 @@ for name in [
|
||||
"HEL-837-交互样本-v2.html",
|
||||
]:
|
||||
copy(args.approved / name, Path("originals") / name)
|
||||
for p in (evidence / "delivery/matrix").rglob("*.png"):
|
||||
copy(p, Path("product") / p.relative_to(evidence / "delivery/matrix"))
|
||||
for p in (evidence / "default-verification/states").glob("*.png"):
|
||||
for p in (evidence / args.matrix_run / "matrix").rglob("*.png"):
|
||||
copy(p, Path("product") / p.relative_to(evidence / args.matrix_run / "matrix"))
|
||||
for p in (evidence / args.states_run / "states").glob("*.png"):
|
||||
copy(p, Path("states") / p.name)
|
||||
for p in (evidence / "motion-hd/motion").glob("*.png"):
|
||||
copy(p, Path("motion") / p.name)
|
||||
for run, name in [("default-verification", "interactions"), ("motion-hd", "motion-hd")]:
|
||||
for run, name in [(args.states_run, "interactions"), ("motion-hd", "motion-hd")]:
|
||||
for i, p in enumerate((evidence / run / "recordings").glob("*.webm")):
|
||||
copy(p, Path("recordings") / f"{name}-{i}.webm")
|
||||
for run, name in [
|
||||
("delivery", "geometry.json"),
|
||||
("delivery", "token-comparison.json"),
|
||||
("default-verification", "interaction-checks.json"),
|
||||
(args.matrix_run, "geometry.json"),
|
||||
(args.matrix_run, "token-comparison.json"),
|
||||
(args.states_run, "interaction-checks.json"),
|
||||
("motion-hd", "motion-checks.json"),
|
||||
("restart", "restart-check.json"),
|
||||
]:
|
||||
@@ -67,7 +69,7 @@ for name in [
|
||||
"motion-840.log",
|
||||
]:
|
||||
copy(root / name, Path("logs") / name)
|
||||
for run in ["delivery", "default-verification", "motion-hd"]:
|
||||
for run in dict.fromkeys([args.matrix_run, args.states_run, "motion-hd"]):
|
||||
copy(evidence / run / "server.log", Path("logs") / f"{run}-server.log")
|
||||
copy(
|
||||
evidence / run / "fixture/provider.jsonl",
|
||||
@@ -79,12 +81,12 @@ for p in (root / "scripts/hel840").glob("*"):
|
||||
copy(p, Path("reproduction/scripts/hel840") / p.name)
|
||||
copy(root / "docs/changelogs/2026-10-24-hel840-v2.md", Path("实现与自测说明.md"))
|
||||
# Fake-only snapshots; never include daemon roster/state or installed configuration.
|
||||
for p in (evidence / "default-verification/fixture/assets/uploads").glob("*"):
|
||||
for p in (evidence / args.states_run / "fixture/assets/uploads").glob("*"):
|
||||
if p.is_file():
|
||||
copy(p, Path("reproduction/fixture/assets/uploads") / p.name)
|
||||
for name in ["before-empty.db", "calliope_config.json"]:
|
||||
copy(
|
||||
evidence / "default-verification/fixture" / name,
|
||||
evidence / args.states_run / "fixture" / name,
|
||||
Path("reproduction/fixture") / name,
|
||||
)
|
||||
head = subprocess.check_output(
|
||||
|
||||
@@ -318,7 +318,6 @@ if not conn.execute("SELECT 1 FROM projects LIMIT 1").fetchone():
|
||||
"name": "暮色",
|
||||
"description": "隔离夹具",
|
||||
"prompt": "夜景中的猫",
|
||||
"negative_prompt": "",
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user