docs(runtime): explain default image sizing grant lifecycle

This commit is contained in:
2026-11-12 14:09:25 +08:00
parent 63c4098e4e
commit 5f2bf2d142
+19 -9
View File
@@ -1,7 +1,7 @@
/**
* 为新建图片保留一次可恢复的自动默认尺寸授权。解码/服务元数据异步到达后,只有创建历史、
* 目标身份/类型、资源引用及初始视觉尺寸仍有效时才允许调整;实际文档/历史改写走专用 planner。
* 用户已提交的手动 resize 撤销授权,即使以后 undo 回到初始尺寸也不自动复活。
* 已判为用户 resize 的授权转为 revoked,正常后续 undo 不自动复活;本模块不负责执行异步 resize 或保存磁盘。
*/
import type { Commit, CoreSnapshot, Id, Registry, Target } from '../canvas-core/types.ts';
import { closeNumber, unitScale } from '../canvas-core/units.ts';
@@ -9,7 +9,9 @@ import { fault, freeze, readMap } from '../canvas-core/values.ts';
import type { EngineSnapshot, RuntimeState } from './contracts.ts';
import type { ResourceRecord } from './resources.ts';
/** 目标代次/创建entry/组件版本和初始视觉尺寸共同限定授权,status/code 描述可恢复结果。 */
/** 以目标代次/创建 entry/组件版本和 width,height÷unitScale 的初值限定一次自动尺寸授权。
* pending 等候元数据/调度,blocked 可由引擎显式重试,applied/revoked 不再由本检查器自动执行。
* 这些状态不等同上传状态或另一个 undo 单元,也不单独证明用户权限/创建历史仍可用。 */
export type DefaultSizeGrant = {
readonly target: Target; readonly entryId: Id; readonly componentId: string; readonly dataVersion: number;
readonly initialSize: { readonly width: number; readonly height: number };
@@ -18,7 +20,9 @@ export type DefaultSizeGrant = {
/** 每资源的版本化授权列表;图片副本可有多个目标,各 grant 独立判断。 */
export type DefaultImageSizing = { readonly version: 1; readonly grants: readonly DefaultSizeGrant[] };
/** 校验解释器、目标/entry/尺寸/状态,并禁止同一 ID+incarnation 的重复 grant。 */
/** 只验 v1、目标/entry/组件版本、正有限初始尺寸、状态/code 与单资源内目标代次唯一性。
* 不查真实 document/history/resource,不证明所有 grant 有效,也不捕获/冻结输入或要求尺寸为整数。
* 未知版本报解释器不可用;保留未来 opaque sizing 的兼容策略由恢复边界处理,不在此按 v1 解释。 */
export function validateDefaultImageSizing(value: unknown): asserts value is DefaultImageSizing {
const state = value as DefaultImageSizing;
if (!state || state.version !== 1) throw fault('default_size_interpreter_unavailable');
@@ -34,7 +38,10 @@ export function validateDefaultImageSizing(value: unknown): asserts value is Def
}
}
/** 从当前创建提交与关联 history 捕获授权,只针对引用该 assetId 的已知且可 resize 节点。 */
/** 从有本 commit 操作贡献的 history 项与 commit 创建变化,匹配当前同代次、同 assetId、已知且能 resize 的 node。
* 保存提交后 width/height÷unitScale,不取文件像素/世界坐标;此时尚不需要 stored 回执或解码尺寸。
* entry 按“含 commit 操作 ID”筛选,不逐项证明该 entry 的 changes 恰含该目标创建;调用方须提供真实创建提交。
* 替换 resource.sizing 为本次 grants(可为空),不合并旧 grant 或做重复校验;完整 grant 校验由发布/恢复边界负责。 */
export function captureDefaultImageSizing(resource: ResourceRecord, next: CoreSnapshot, commit: Commit, registry: Registry): ResourceRecord {
const grants: DefaultSizeGrant[] = [];
for (const entry of next.history.entries.values()) {
@@ -54,10 +61,11 @@ export function captureDefaultImageSizing(resource: ResourceRecord, next: CoreSn
return freeze({ ...resource, sizing: { version: 1, grants } });
}
/** Runs on every publication path. Revocation is durable and does not revive when
* undo happens to return to the default geometry. Preview-only gestures do not publish.
*/
/** 每条 Root 发布路径检查 pending/blocked grant;失效时持久标 revoked,未变化则复用 runtime。 */
/** Return a revocation candidate from each publication path; the engine persists the Root. */
/** 对 next 的 v1 pending/blocked 授权检查身份/类型/资源、创建项仍在 undo、归一化尺寸和新用户意图。
* 失效则返回带 revoked/code 的 copy-on-write runtime,不执行 resize/网络/磁盘;未变化复用 next.runtime。
* applied/revoked 输入直接保留,未来版本不解释;不独自阻止受信调用方手动改写这些状态,状态所有权由引擎管理。
* 不读取 registry/手势/解码结果,真正消费 grant 的资格与尺寸计划由引擎随后复核。 */
export function prepareDefaultImageSizing(previous: EngineSnapshot, next: EngineSnapshot): RuntimeState {
let resources = next.runtime.resources;
for (const [id, resource] of resources) {
@@ -76,7 +84,9 @@ export function prepareDefaultImageSizing(previous: EngineSnapshot, next: Engine
if (!closeNumber(element.geometry.width / units, grant.initialSize.width) || !closeNumber(element.geometry.height / units, grant.initialSize.height)) code = 'user_resized';
// Detect committed resize-away-and-back transactions. Pure coordinate
// conversions may alter geometry sizes and unitScale together.
// 已提交事务即使最终尺寸回原值,也检查用户 geometry changes;纯 owner/unit 转换例外。
// 在文档变化且 operations 数量增加时扫描新 user 意图,识别 resize-away-and-back;假设操作表按正常发布追加。
// 同一意图含 owner 字段或 unitScale 变化就整体按转换豁免此附加检查;不逐轴拆分任意混合转换/手动缩放。
// 净归一化尺寸已改变仍会在上方撤销;纯预览不发布 operations,因此不会因 pointermove 单独触发。
if (!code && previous.document !== next.document && previous.operations.size !== next.operations.size) {
for (const [operationId, intent] of next.operations) {
if (previous.operations.has(operationId) || intent.origin !== 'user') continue;