清理完美像素链路的死代码
- 删除只有测试调用的 snap_pixel_art_strict 与 snap_pixel_art 两个无 deadline 包装,公开入口收敛为 snap_pixel_art_with_deadline 与 snap_pixel_art_strict_with_deadline;原本只写在 snap_pixel_art 头上的双输入 尺寸与输出契约合并进保留者的文档,调用点改为显式传 None。 - 去掉持久化结果里从不被读取的 status 透传(客户端枚举与 record 字段)。该段 原本搭载了「status 缺失即拒绝」的校验,保留为显式 is_none 拦截,并补一个 此前缺失的回归用例锁住它。 - 快速编辑面板不再对 dialog 状态做 pending-confirmation 的运行时收窄:dialog 由面板派生,写回方只做 failed→idle 重置,该分支恒不成立,直接取面板状态。 - 收回三个无人导入的 export(reconcilePerfectPixelProject、 createPerfectPixelReconciliationOperation、 INVALID_PERFECT_PIXEL_OPERATION_ERROR_MESSAGE)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,8 @@ pub mod vector_engine;
|
||||
|
||||
pub use pixel_art_snapper::{
|
||||
PIXEL_ART_ALPHA_COVERAGE_THRESHOLD, PIXEL_ART_ANALYSIS_COLORS, PIXEL_ART_KMEANS_SAMPLE_LIMIT,
|
||||
PIXEL_ART_MAX_IMAGE_PIXELS, PixelArtSnapError, snap_pixel_art, snap_pixel_art_strict,
|
||||
snap_pixel_art_strict_with_deadline, snap_pixel_art_with_deadline,
|
||||
PIXEL_ART_MAX_IMAGE_PIXELS, PixelArtSnapError, snap_pixel_art_strict_with_deadline,
|
||||
snap_pixel_art_with_deadline,
|
||||
};
|
||||
pub use vector_engine::{
|
||||
DownloadedImage, GPT_IMAGE_2_C_MODEL, GPT_IMAGE_2_MODEL, GeneratedImages, NANOBANANA_2_MODEL,
|
||||
|
||||
@@ -156,31 +156,11 @@ impl SnapConfig {
|
||||
/// image is always a PNG at the original `rgba_source` dimensions. Its alpha
|
||||
/// channel contains only `0` or `255`, and fully transparent pixels are
|
||||
/// canonical `[0, 0, 0, 0]`.
|
||||
pub fn snap_pixel_art(
|
||||
grid_source: &DownloadedImage,
|
||||
rgba_source: &DownloadedImage,
|
||||
) -> Result<DownloadedImage, PixelArtSnapError> {
|
||||
snap_pixel_art_with_deadline(grid_source, rgba_source, None)
|
||||
}
|
||||
|
||||
/// Snap an image unless legacy analysis detects no grid step on either axis.
|
||||
///
|
||||
/// Unlike [`snap_pixel_art`], this entry does not synthesize a uniform
|
||||
/// min-dimension/64 grid when neither axis contains a detectable step. Every
|
||||
/// other detection, walking, sampling, and encoding behavior is identical.
|
||||
pub fn snap_pixel_art_strict(
|
||||
grid_source: &DownloadedImage,
|
||||
rgba_source: &DownloadedImage,
|
||||
) -> Result<DownloadedImage, PixelArtSnapError> {
|
||||
snap_pixel_art_strict_with_deadline(grid_source, rgba_source, None)
|
||||
}
|
||||
|
||||
/// Deadline-aware variant of [`snap_pixel_art`].
|
||||
///
|
||||
/// The deadline is checked before and after non-cooperative codec/resize
|
||||
/// operations, and periodically inside the K-means, profile, and cell-sampling
|
||||
/// loops. Exceeding it returns [`PixelArtSnapError::DeadlineExceeded`] without
|
||||
/// producing a partial image.
|
||||
/// producing a partial image. Pass `None` to opt out of deadline checks.
|
||||
pub fn snap_pixel_art_with_deadline(
|
||||
grid_source: &DownloadedImage,
|
||||
rgba_source: &DownloadedImage,
|
||||
@@ -189,7 +169,12 @@ pub fn snap_pixel_art_with_deadline(
|
||||
snap_pixel_art_with_grid_policy(grid_source, rgba_source, deadline, false)
|
||||
}
|
||||
|
||||
/// Deadline-aware variant of [`snap_pixel_art_strict`].
|
||||
/// Snap an image unless legacy analysis detects no grid step on either axis.
|
||||
///
|
||||
/// Unlike [`snap_pixel_art_with_deadline`], this entry does not synthesize a
|
||||
/// uniform min-dimension/64 grid when neither axis contains a detectable step.
|
||||
/// Every other detection, walking, sampling, and encoding behavior — including
|
||||
/// the deadline semantics — is identical.
|
||||
pub fn snap_pixel_art_strict_with_deadline(
|
||||
grid_source: &DownloadedImage,
|
||||
rgba_source: &DownloadedImage,
|
||||
@@ -1111,7 +1096,8 @@ mod tests {
|
||||
let grid = downloaded_png(RgbaImage::from_pixel(8, 8, Rgba([10, 20, 30, 255])));
|
||||
let rgba = downloaded_png(RgbaImage::from_pixel(8, 9, Rgba([10, 20, 30, 255])));
|
||||
|
||||
let error = snap_pixel_art(&grid, &rgba).expect_err("dimensions should mismatch");
|
||||
let error = snap_pixel_art_with_deadline(&grid, &rgba, None)
|
||||
.expect_err("dimensions should mismatch");
|
||||
assert!(
|
||||
error
|
||||
.to_string()
|
||||
@@ -1141,9 +1127,9 @@ mod tests {
|
||||
fn strict_mode_rejects_only_when_legacy_would_use_uniform_fallback() {
|
||||
let source = downloaded_png(RgbaImage::from_pixel(128, 128, Rgba([10, 20, 30, 255])));
|
||||
|
||||
let legacy = snap_pixel_art(&source, &source)
|
||||
let legacy = snap_pixel_art_with_deadline(&source, &source, None)
|
||||
.expect("legacy generation style should retain its uniform fallback");
|
||||
let strict = snap_pixel_art_strict(&source, &source)
|
||||
let strict = snap_pixel_art_strict_with_deadline(&source, &source, None)
|
||||
.expect_err("explicit strict action should reject an undetected grid");
|
||||
|
||||
assert_eq!(decode_output(&legacy).dimensions(), (128, 128));
|
||||
@@ -1179,9 +1165,9 @@ mod tests {
|
||||
);
|
||||
|
||||
let source = downloaded_png(image);
|
||||
let legacy = snap_pixel_art(&source, &source)
|
||||
let legacy = snap_pixel_art_with_deadline(&source, &source, None)
|
||||
.expect("legacy processing should succeed with a detected step");
|
||||
let strict = snap_pixel_art_strict(&source, &source)
|
||||
let strict = snap_pixel_art_strict_with_deadline(&source, &source, None)
|
||||
.expect("strict processing should reuse the detected legacy step");
|
||||
assert_eq!(strict.bytes, legacy.bytes);
|
||||
assert_eq!(strict.mime_type, legacy.mime_type);
|
||||
@@ -1208,9 +1194,10 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let output = snap_pixel_art(
|
||||
let output = snap_pixel_art_with_deadline(
|
||||
&downloaded_png(grid_image),
|
||||
&downloaded_png(rgba_image.clone()),
|
||||
None,
|
||||
)
|
||||
.expect("pixel snapping should succeed");
|
||||
let decoded = decode_output(&output);
|
||||
@@ -1237,8 +1224,10 @@ mod tests {
|
||||
}
|
||||
let rgba = downloaded_png(rgba);
|
||||
|
||||
let first = snap_pixel_art(&grid, &rgba).expect("first snap should succeed");
|
||||
let second = snap_pixel_art(&grid, &rgba).expect("second snap should succeed");
|
||||
let first =
|
||||
snap_pixel_art_with_deadline(&grid, &rgba, None).expect("first snap should succeed");
|
||||
let second =
|
||||
snap_pixel_art_with_deadline(&grid, &rgba, None).expect("second snap should succeed");
|
||||
assert_eq!(first.bytes, second.bytes);
|
||||
|
||||
for pixel in decode_output(&first).pixels() {
|
||||
|
||||
@@ -30,16 +30,8 @@ pub struct EditorPixelArtResultPersistRecordInput {
|
||||
pub completed_at_micros: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum EditorPixelArtResultPersistRecordStatus {
|
||||
Applied,
|
||||
DialogMissing,
|
||||
AlreadyApplied,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct EditorPixelArtResultPersistRecord {
|
||||
pub status: EditorPixelArtResultPersistRecordStatus,
|
||||
pub asset_object: module_assets::AssetObjectUpsertSnapshot,
|
||||
pub project_resource: EditorProjectResourceRecord,
|
||||
pub asset: EditorAssetRecord,
|
||||
@@ -1103,20 +1095,13 @@ fn map_editor_pixel_art_result_persist_result(
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::procedure_failed(result.error_message));
|
||||
}
|
||||
let status = match result
|
||||
.status
|
||||
.ok_or_else(|| SpacetimeClientError::validation_failed("完美像素持久化结果缺少状态"))?
|
||||
{
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::Applied => {
|
||||
EditorPixelArtResultPersistRecordStatus::Applied
|
||||
}
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::DialogMissing => {
|
||||
EditorPixelArtResultPersistRecordStatus::DialogMissing
|
||||
}
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::AlreadyApplied => {
|
||||
EditorPixelArtResultPersistRecordStatus::AlreadyApplied
|
||||
}
|
||||
};
|
||||
// 中文注释:三种落库状态(Applied / DialogMissing / AlreadyApplied)目前没有任何
|
||||
// 上层消费方,不再向上透传;但状态缺失说明 procedure 返回体不完整,仍要拦在这里。
|
||||
if result.status.is_none() {
|
||||
return Err(SpacetimeClientError::validation_failed(
|
||||
"完美像素持久化结果缺少状态",
|
||||
));
|
||||
}
|
||||
let asset_object = result.asset_object.ok_or_else(|| {
|
||||
SpacetimeClientError::validation_failed("完美像素持久化结果缺少 asset object")
|
||||
})?;
|
||||
@@ -1141,7 +1126,6 @@ fn map_editor_pixel_art_result_persist_result(
|
||||
.flatten();
|
||||
|
||||
Ok(EditorPixelArtResultPersistRecord {
|
||||
status,
|
||||
asset_object: map_editor_spritesheet_asset_object_snapshot(asset_object),
|
||||
project_resource: map_editor_spritesheet_project_resource_snapshot(project_resource)?,
|
||||
asset: map_editor_spritesheet_asset_snapshot(asset)?,
|
||||
@@ -1422,29 +1406,36 @@ mod pixel_art_persist_mapper_tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_pixel_art_persist_mapper_preserves_status_and_accepts_missing_project() {
|
||||
fn editor_pixel_art_persist_mapper_rejects_missing_status() {
|
||||
let mut result = successful_pixel_art_persist_result(
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::Applied,
|
||||
);
|
||||
result.status = None;
|
||||
|
||||
let error = map_editor_pixel_art_result_persist_result(result)
|
||||
.expect_err("成功结果缺少状态时必须拒绝");
|
||||
|
||||
assert!(matches!(
|
||||
error,
|
||||
SpacetimeClientError::Runtime(message)
|
||||
if message == "完美像素持久化结果缺少状态"
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editor_pixel_art_persist_mapper_accepts_every_status_and_missing_project() {
|
||||
let cases = [
|
||||
(
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::Applied,
|
||||
EditorPixelArtResultPersistRecordStatus::Applied,
|
||||
),
|
||||
(
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::DialogMissing,
|
||||
EditorPixelArtResultPersistRecordStatus::DialogMissing,
|
||||
),
|
||||
(
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::AlreadyApplied,
|
||||
EditorPixelArtResultPersistRecordStatus::AlreadyApplied,
|
||||
),
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::Applied,
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::DialogMissing,
|
||||
crate::module_bindings::EditorPixelArtResultPersistStatus::AlreadyApplied,
|
||||
];
|
||||
|
||||
for (binding_status, expected_status) in cases {
|
||||
for binding_status in cases {
|
||||
let mapped = map_editor_pixel_art_result_persist_result(
|
||||
successful_pixel_art_persist_result(binding_status),
|
||||
)
|
||||
.expect("project=None 仍应映射成功");
|
||||
|
||||
assert_eq!(mapped.status, expected_status);
|
||||
assert_eq!(mapped.asset_object.asset_object_id, "assetobj_pixel");
|
||||
assert_eq!(mapped.project_resource.resource_id, "editor-resource-pixel");
|
||||
assert_eq!(mapped.asset.asset_id, "editor-asset-pixel");
|
||||
|
||||
@@ -342,7 +342,7 @@ export const PERFECT_PIXEL_RECONCILIATION_WINDOW_MS = 75_000;
|
||||
// 中文注释:第一批曾把 v1 快照写成 240 秒。滚动部署与旧标签页仍可能持久化该形状,
|
||||
// 所以读取侧保留兼容上限;它只决定快照是否可信,不会延长当前 75 秒对账窗口。
|
||||
const LEGACY_PERFECT_PIXEL_RECONCILIATION_WINDOW_MS = 240_000;
|
||||
export const INVALID_PERFECT_PIXEL_OPERATION_ERROR_MESSAGE =
|
||||
const INVALID_PERFECT_PIXEL_OPERATION_ERROR_MESSAGE =
|
||||
'完美像素操作快照无效,禁止自动重试。';
|
||||
|
||||
const PERFECT_PIXEL_OPERATION_KEYS = new Set([
|
||||
|
||||
@@ -78,10 +78,11 @@ function syncPanelFromDialogUpdate(
|
||||
model: normalizedDialog.imageModel ?? normalizedPanel.model,
|
||||
quickEditReferences: normalizedDialog.generationReferences,
|
||||
assetLabel: normalizedDialog.assetLabel,
|
||||
status:
|
||||
normalizedDialog.status === 'pending-confirmation'
|
||||
? normalizedPanel.status
|
||||
: normalizedDialog.status,
|
||||
// 中文注释:dialog 由 createQuickEditDialog 从面板自身派生,组合器只会做
|
||||
// failed→idle 的重置,不会写入 'pending-confirmation' 这类完美像素专属状态。
|
||||
// 因此归一化后的 dialog 状态恒等于归一化后的面板状态,直接取面板侧即可,
|
||||
// 也让面板的三态联合类型不必在这里做运行时收窄。
|
||||
status: normalizedPanel.status,
|
||||
errorMessage: normalizedDialog.errorMessage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ export function inspectPerfectPixelProjectSnapshot(
|
||||
return { kind: 'applied', project, resource };
|
||||
}
|
||||
|
||||
export function createPerfectPixelReconciliationOperation(
|
||||
function createPerfectPixelReconciliationOperation(
|
||||
operation: PerfectPixelOperationSnapshot,
|
||||
startedAt = Date.now(),
|
||||
): PerfectPixelOperationSnapshot {
|
||||
@@ -362,7 +362,7 @@ function waitForPerfectPixelReconciliationDelay(
|
||||
});
|
||||
}
|
||||
|
||||
export async function reconcilePerfectPixelProject(
|
||||
async function reconcilePerfectPixelProject(
|
||||
projectId: string,
|
||||
operation: PerfectPixelOperationSnapshot,
|
||||
options: { signal?: AbortSignal } = {},
|
||||
|
||||
Reference in New Issue
Block a user