@hankchen/canvas-editor 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { LayerBox, Canvas, TextLayoutPolicies, Layer, LayerType, TableLayer, RenderBackend, Padding, HorizontalAlign, VerticalAlign, Anchor, TableRowLayer, TableCellLayer, TableRowTemplateLayer } from '@hankchen/canvas';
2
- export { ANCHORS, Anchor, Border, BorderSide, Canvas, HORIZONTAL_ALIGNS, HorizontalAlign, LAYER_TYPES, Layer, LayerBox, LayerType, Padding, TableCellLayer, TableLayer, TableRowLayer, TextLayer, VERTICAL_ALIGNS, VerticalAlign } from '@hankchen/canvas';
1
+ import { LayerBox, Canvas, TextLayoutPolicies, Layer, LayerType, TableLayer, RenderBackend, Padding, HorizontalAlign, VerticalAlign, CanvasBackground, Anchor, TableRowLayer, TableCellLayer, TableRowTemplateLayer } from '@hankchen/canvas';
2
+ export { ANCHORS, Anchor, Border, BorderSide, Canvas, CanvasBackground, HORIZONTAL_ALIGNS, HorizontalAlign, LAYER_TYPES, Layer, LayerBox, LayerType, Padding, TableCellLayer, TableLayer, TableRowLayer, TextLayer, VERTICAL_ALIGNS, VerticalAlign } from '@hankchen/canvas';
3
3
  import { Draft, Patch } from 'immer';
4
4
  export { canvasFontCssFamily } from '@hankchen/canvas-browser-renderer';
5
5
 
@@ -958,6 +958,12 @@ interface FindSession {
958
958
  replacement: string;
959
959
  cursor: number;
960
960
  }
961
+ /** 数据源声明拒绝详情(声明被拒呈现面,死端态整改 2b):reason 为声明期拒绝码、
962
+ * detail 为教学性明细(直取 parseExpressionSchema 拒绝面) */
963
+ interface DataSourceSchemaRejection {
964
+ reason: ExpressionSchemaRejectReason;
965
+ detail: string;
966
+ }
961
967
  interface EditorUi {
962
968
  viewport: Viewport;
963
969
  /** 当前选中图层路径(数组路径,patch path 前缀);null = 无选择 */
@@ -1027,6 +1033,14 @@ interface EditorUi {
1027
1033
  * 文档不重置(声明随会话,重注入/清除走同一入口)。
1028
1034
  */
1029
1035
  dataSourceSchema: ExpressionSchemaNode | null;
1036
+ /**
1037
+ * 数据源声明拒绝详情(声明被拒呈现面,死端态整改 2b):仅被拒注入在场——
1038
+ * reason/detail 直取 parseExpressionSchema 拒绝面(reserved_root_key /
1039
+ * invalid_schema / open_map_unsupported),宿主读数与绑定面据此区分「未注入」
1040
+ * 与「注入被拒」。只住会话态随 dataSourceSchema 同步置换:合法注入与清除
1041
+ * 归 null;不进 graph、不落 localStorage、不进 wire。
1042
+ */
1043
+ dataSourceSchemaRejection: DataSourceSchemaRejection | null;
1030
1044
  /**
1031
1045
  * 参考线(ruler-guides-snap 工单 01,ADR 0012):使用者从标尺拖出的会话级
1032
1046
  * 对位轴(水平/垂直、场景坐标)。零契约面——不进历史、不写 graph、保存零
@@ -1169,10 +1183,11 @@ declare class EditorStore {
1169
1183
  setNextTextStyle(patch: Partial<NextTextStyle>): void;
1170
1184
  /**
1171
1185
  * 数据源 schema 声明替换(归一产物或降级 null,校验收口在注入缝不在这里):
1172
- * 同引用短路(重复注入同一归一产物不惊动订阅方);仅 ui 通知,不参与重绘
1186
+ * 声明与拒绝详情成对置换(rejection 仅被拒注入在场,2b 呈现面),同引用且
1187
+ * 同拒绝态短路(重复注入同一归一产物不惊动订阅方);仅 ui 通知,不参与重绘
1173
1188
  * 脏标(onStoreChange 对该分支短路)。
1174
1189
  */
1175
- setDataSourceSchema(schema: ExpressionSchemaNode | null): void;
1190
+ setDataSourceSchema(schema: ExpressionSchemaNode | null, rejection?: DataSourceSchemaRejection | null): void;
1176
1191
  /** 参考线列表替换(增删动作在会话门面收口);仅 ui 通知,不参与历史 */
1177
1192
  setGuides(guides: readonly Guide[]): void;
1178
1193
  /** 标尺显隐:布尔值等短路(重复置同态不重绘) */
@@ -2048,8 +2063,11 @@ declare class EditorSession {
2048
2063
  updateDataExpression(path: LayerPath, expression: string | null, options?: TransactOptions): void;
2049
2064
  /**
2050
2065
  * 数据字段取值方式切换(静态 ⇆ 表达式,工单 02 自面板收编内核):表达式现态
2051
- * → 字面接管(updateData,值保持现镜像);静态现态 → 打标(updateDataExpression,
2052
- * 初值 = 值字段原文)。各为独立一步历史(无 mergeKey)。三内容层分派各取值
2066
+ * → 字面接管(updateData,值保持现镜像);静态现态 → 打标,初值分内容面——
2067
+ * 文本层镜像原文(字面可含合法插值混排),资源层(Image/Qr)只镜像**已合法
2068
+ * 的单引用字面**(静态 ⇆ 表达式往返无损),违例字面(URL/拼接文案)落
2069
+ * 「未选择」空态不镜像——切换自身不得制造必违例初值(死端态整改条目 4;旧
2070
+ * 值可经撤销一步找回)。各为独立一步历史(无 mergeKey)。三内容层分派各取值
2053
2071
  * 字段,表/行/格/悬空路径空转。
2054
2072
  *
2055
2073
  * 活标志防御路由(ADR 0019):路径即编辑中的层时只翻会话活标志(零文档变更
@@ -2057,6 +2075,8 @@ declare class EditorSession {
2057
2075
  * 草稿;面板锁定段/pill 显式调用与宿主旁路调用都安全收敛到同一语义。
2058
2076
  */
2059
2077
  toggleDataMode(path: LayerPath): void;
2078
+ /** 资源面切换初值:字面已是合法单引用(含空串为「未选择」)才镜像,违例落空态 */
2079
+ private resourceToggleInitial;
2060
2080
  /**
2061
2081
  * 内容写事务的统一形(会话内容独占工单 02):transact 落库 + 命中编辑层的
2062
2082
  * 草稿接管收口——updateData/updateDataExpression/replaceOne/replaceAll 四个
@@ -2070,8 +2090,15 @@ declare class EditorSession {
2070
2090
  * commitTextEdit 先清会话再落库,天然不复入接管。
2071
2091
  */
2072
2092
  private takeoverDraftIfNeeded;
2073
- /** 更新画布级字段(未选中图层时面板的宽/高写入口);键由类型收窄为 width/height */
2093
+ /**
2094
+ * 更新画布级字段(未选中图层时面板的写入口):width/height 数值;background
2095
+ * 整对象一步事务(canvas-background 工单 04)——面板各分量提交折算回整对象,
2096
+ * 归一经 normalizeCanvasBackground(空串分量归 null、两分量皆空 = 清背景 null,
2097
+ * 镜像解码语义;条件写键归 encode);结构相同的整对象空转(immer 引义换新
2098
+ * 不产生历史步,数值键的零变更语义同门)。
2099
+ */
2074
2100
  updateCanvasProp(key: 'width' | 'height', value: number, options?: TransactOptions): void;
2101
+ updateCanvasProp(key: 'background', value: CanvasBackground | null, options?: TransactOptions): void;
2075
2102
  /**
2076
2103
  * 注入数据源 schema 声明(载荷形态,D1 钉定:声明即 compile(canvas, dataset)
2077
2104
  * 收到的 data 载荷形状,根上下文候选 = 载荷顶层键)。声明经
@@ -3124,6 +3151,16 @@ declare function enumerateExpressionCandidates(schema: ExpressionSchemaNode | nu
3124
3151
  * 给根 schema 表根起点,故独立落件不改 expressionContext);任一层解析不出
3125
3152
  * (rowsPath 无 items 声明/漂移/途经非表)→ null 降级无候选。
3126
3153
  *
3154
+ * 点选器剪枝枚举件(rows-path-picker 工单 01,spec D5/D3):级联点选器每层的
3155
+ * 候选条目——在补全枚举语义上加剪枝谓词 leadsToArray,候选面收敛至「通往行数组
3156
+ * 的路径」:标量子段不出现;对象子段仅当子树内沿 properties 链至少含一个可达
3157
+ * 数组才出现;数组子段 = 终点条目(isArray 标记),不透传 items(数组套数组的
3158
+ * 内层不可达)。无 partial 概念(纯点选无输入期),前缀 = 已积累点路径、逐段
3159
+ * 全量已消费、尾段取子层;空层/漂移/畸形前缀一律返回空数组(空态呈现归 vue
3160
+ * 面)。起点判别不在此重复造:调用方经 resolveRowsPathStartSchema 得起点后再
3161
+ * 进本件。元信息同表达式补全先例(D8):description/title 两字段分离透传,展示
3162
+ * 回落 description ?? title 归呈现层。
3163
+ *
3127
3164
  * 全模块纯函数、零 DOM;层内依赖仅 expressionSchema / expressionCandidates
3128
3165
  * (候选类型面)/ layerPath。
3129
3166
  */
@@ -3146,6 +3183,31 @@ interface RowsPathCandidateResult {
3146
3183
  * 阻);枚举落点为对象层 → 该层全量合法子项。
3147
3184
  */
3148
3185
  declare function enumerateRowsPathCandidates(startSchema: ExpressionSchemaNode | null, input: string): RowsPathCandidateResult | null;
3186
+ /**
3187
+ * 一条 rowsPath 点选器条目(rows-path-picker 工单 01):级联一层的可点项。
3188
+ * 与 RowsPathCandidate 的分叉:无 partial/徽标面,换显式数组终点标记——点选器
3189
+ * 按 isArray 分流「点选提交 / 只可下钻」,不再经「带尾点探一层」推断。
3190
+ */
3191
+ interface RowsPathPickerEntry {
3192
+ /** 提交/下钻的完整点路径(起点相对;点选器 v-model 面就是它) */
3193
+ path: string;
3194
+ /** 本段名(无元信息时的显示回落) */
3195
+ segment: string;
3196
+ /** 显示名注解(D8 两字段分离透传;展示回落 description ?? title 归呈现层) */
3197
+ title?: string;
3198
+ /** 说明(未声明为 undefined) */
3199
+ description?: string;
3200
+ /** 数组终点标记:true = 行数组终点(点选即提交);false = 对象中间站(只可下钻) */
3201
+ isArray: boolean;
3202
+ }
3203
+ /**
3204
+ * 枚举点选器某层的候选条目(rows-path-picker 工单 01):prefix 为已积累点路径,
3205
+ * 逐段全量已消费、尾段取子层('' = 起点层);下钻域规则走 descendToLayer,每层
3206
+ * 条目经 leadsToArray 剪枝至「通往行数组的路径」。起点 schema null / 前缀漂移
3207
+ * (段缺失/标量落点)/ 畸形(双点/尾点/头点)/ 数组落点(数组不展开)→ 空数组,
3208
+ * 全程不抛错——空态呈现归 vue 面(spec D6)。
3209
+ */
3210
+ declare function enumerateRowsPathPickerEntries(startSchema: ExpressionSchemaNode | null, prefix: string): readonly RowsPathPickerEntry[];
3149
3211
  /**
3150
3212
  * rowsPath 编辑起点的三分流判别(D3):无 template 祖先 → 载荷根 schema(根层
3151
3213
  * 表;建表表单恒根起点同款);有 template 祖先 → 沿选中路径每个 template 段行
@@ -3201,4 +3263,4 @@ declare function memoizeTextPolicies(policies?: TextLayoutPolicies, options?: Me
3201
3263
  */
3202
3264
  declare const PACKAGE_NAME: "@hankchen/canvas-editor";
3203
3265
 
3204
- export { ALIGN_CORNER_MARGIN_PX, ALT_DRAG_DEAD_ZONE_SCREEN_PX, ARM_CREATE_LAYER_TYPES, type AlignBoxGeometry, type AlignToCanvasMode, type AlignToCanvasOptions, type ArmCreateShortcutAction, CREATE_DEAD_ZONE_SCREEN_PX, type CanvasEditorState, type CreateGesture, type CreateRectResult, DATE_FORMAT_SUGGESTIONS, DEFAULT_EDITOR_SHORTCUTS, DEFAULT_ZOOM_BOUNDS, type DeletedLayerRef, type DocRecipe, type DragGesture, EDITOR_STATE_SCHEMA_VERSION, EXPRESSION_FUNCTIONS, type EditorChange, type EditorPrefsState, EditorSession, type EditorSessionOptions, type EditorShortcutAction, type EditorShortcutBinding, type EditorShortcutGroup, type EditorShortcutInput, EditorStore, type EditorUi, type ExpressionCallArg, type ExpressionCallErrorKind, type ExpressionCallParse, type ExpressionCallSlotContext, type ExpressionCandidate, type ExpressionCandidateQuery, type ExpressionCandidateResult, type ExpressionContextKind, type ExpressionErrorRange, type ExpressionFormatSuggestion, type ExpressionFragmentDiagnostic, type ExpressionFragmentErrorKind, type ExpressionFunctionMeta, type ExpressionPathHead, type ExpressionPathParse, type ExpressionScan, type ExpressionScanFragment, type ExpressionScanLiteral, type ExpressionScanOpen, type ExpressionScanPart, type ExpressionSchemaChildEntry, type ExpressionSchemaChildView, type ExpressionSchemaDiagnostic, type ExpressionSchemaDiagnosticCode, type ExpressionSchemaNode, type ExpressionSchemaParse, type ExpressionSchemaRejectReason, type ExpressionSyntaxError, type FindSession, type FindTextHit, FontCatalog, type FontCatalogEntry, type FrameScheduler, type Guide, type GuideOrientation, type HistoryStep, type HitTestOptions, type InvalidateTarget, type LayerOutlineNode, type LayerOutlineRole, type LayerPath, MAX_HISTORY_STEPS, type MemoTextPoliciesOptions, type MovedSubtreeRef, NUMBER_FUNCTION_SUGGESTIONS, type NextTextStyle, type OverlayPaintArgs, type OverlayPainter, PACKAGE_NAME, PAN_TO_BOX_MARGIN_PX, PASTE_OFFSET_PX, PHP_TRIM_CHARS, type Point, type ProportionalDrivingAxis, RATIO_PRESETS, RESIZE_HANDLES, RESIZE_HANDLE_HIT_PX, RESIZE_MIN_SIZE_PX, type RatioPreset, type RatioPresetId, type Rect, type ResizeAxes, type ResizeBoxGeometry, type ResizeBoxResult, type ResizeHandle, type ResourceStatus, type ResourceStatusMap, type RowsPathCandidate, type RowsPathCandidateResult, SNAP_THRESHOLD_SCREEN_PX, STYLE_FIELD_KEYS_BY_TYPE, type ShortcutCombo, type Size, type SnapAxis, type SnapAxisSource, type SnapBoxGeometry, type SnapPoints, type SnapResolution, type StyleSnapshot, TEXT_PRESETS, type TextEditLayout, type TextEditingSession, type TextPreset, type TextPresetId, type TransactOptions, type UploadFile, type UploadHandler, UploadHandlerMissingError, type UploadImagePlacement, type Viewport, WHEEL_ZOOM_STEP, type WheelInput, type WheelIntent, type ZoomBounds, addRootLayerInDraft, addTableCellInDraft, addTableRowInDraft, addTemplateCellInDraft, alignToCanvasTarget, applyStyleFieldsInDraft, buildLayerOutline, canCopyLayerAt, canonicalizeTableSyncInDraft, captureStyleSnapshot, clampZoom, classifyEditorShortcut, classifyWheel, cloneLayerSubtree, convertTableToRowsInDraft, convertTableToTemplateInDraft, createDefaultLayer, createRubberBandRect, createTemplateTable, defaultNextTextStyle, defaultSizeForRatio, deleteLayerInDraft, enumerateExpressionCandidates, enumerateRowsPathCandidates, expressionCallSlotAtEnd, expressionDiagnostics, expressionFragmentAtCursor, findPresetEntry, fitRect, fitViewport, growRowToCellInDraft, handleResizesHeight, handleResizesWidth, hitTest, insertRootLayerAdjacentInDraft, insertRootLayerInDraft, isCallFragment, isImageContentUndrawn, isLayerPath, isLockedPath, isRootLayerPath, isRootPasteableType, isTemplateSubtreePath, layerBoxByPath, memoizeTextPolicies, moveGuard, moveRootLayerInDraft, moveTableCellInDraft, moveTableCellToRowInDraft, moveTableRowInDraft, moveTableRowToTableInDraft, nextZoomByWheel, normalizeExpressionSchemaSource, panBy, panToBox, parseExpressionCall, parseExpressionPath, parseExpressionSchema, pastePosition, pathStartsWith, pathsEqual, phpTrim, pickNextTextStylePatch, prepareRootPaste, proportionalDrivingAxis, ratioValue, readSpecField, remapPathAfterSplice, replaceHitsInDraft, resizableAxesAt, resizeBox, resizeBoxProportional, resizeHandleAt, resizeHandlePoint, resizeHandlesAt, resizeProportionalSnapPoints, resizeSnapPoints, resolveLayer, resolveRowSchema, resolveRowsPathStartSchema, resolveSnap, resolveSnapPoints, resourcePathDiagnostics, rootLayerOf, scanExpressionFragments, scanTextMatches, sceneToScreen, schemaChildEntries, schemaNodeAtPath, screenToScene, selectionParentPath, setCellAutoHeightInDraft, snapAxesFromBoxes, snapThresholdScene, snapViewportToPhysicalPixels, syncContentIntoCellInDraft, syncRowWidthInDraft, uploadDisplayName, visibleRootBoxes, writeSpecField, zoomAtPoint };
3266
+ export { ALIGN_CORNER_MARGIN_PX, ALT_DRAG_DEAD_ZONE_SCREEN_PX, ARM_CREATE_LAYER_TYPES, type AlignBoxGeometry, type AlignToCanvasMode, type AlignToCanvasOptions, type ArmCreateShortcutAction, CREATE_DEAD_ZONE_SCREEN_PX, type CanvasEditorState, type CreateGesture, type CreateRectResult, DATE_FORMAT_SUGGESTIONS, DEFAULT_EDITOR_SHORTCUTS, DEFAULT_ZOOM_BOUNDS, type DataSourceSchemaRejection, type DeletedLayerRef, type DocRecipe, type DragGesture, EDITOR_STATE_SCHEMA_VERSION, EXPRESSION_FUNCTIONS, type EditorChange, type EditorPrefsState, EditorSession, type EditorSessionOptions, type EditorShortcutAction, type EditorShortcutBinding, type EditorShortcutGroup, type EditorShortcutInput, EditorStore, type EditorUi, type ExpressionCallArg, type ExpressionCallErrorKind, type ExpressionCallParse, type ExpressionCallSlotContext, type ExpressionCandidate, type ExpressionCandidateQuery, type ExpressionCandidateResult, type ExpressionContextKind, type ExpressionErrorRange, type ExpressionFormatSuggestion, type ExpressionFragmentDiagnostic, type ExpressionFragmentErrorKind, type ExpressionFunctionMeta, type ExpressionPathHead, type ExpressionPathParse, type ExpressionScan, type ExpressionScanFragment, type ExpressionScanLiteral, type ExpressionScanOpen, type ExpressionScanPart, type ExpressionSchemaChildEntry, type ExpressionSchemaChildView, type ExpressionSchemaDiagnostic, type ExpressionSchemaDiagnosticCode, type ExpressionSchemaNode, type ExpressionSchemaParse, type ExpressionSchemaRejectReason, type ExpressionSyntaxError, type FindSession, type FindTextHit, FontCatalog, type FontCatalogEntry, type FrameScheduler, type Guide, type GuideOrientation, type HistoryStep, type HitTestOptions, type InvalidateTarget, type LayerOutlineNode, type LayerOutlineRole, type LayerPath, MAX_HISTORY_STEPS, type MemoTextPoliciesOptions, type MovedSubtreeRef, NUMBER_FUNCTION_SUGGESTIONS, type NextTextStyle, type OverlayPaintArgs, type OverlayPainter, PACKAGE_NAME, PAN_TO_BOX_MARGIN_PX, PASTE_OFFSET_PX, PHP_TRIM_CHARS, type Point, type ProportionalDrivingAxis, RATIO_PRESETS, RESIZE_HANDLES, RESIZE_HANDLE_HIT_PX, RESIZE_MIN_SIZE_PX, type RatioPreset, type RatioPresetId, type Rect, type ResizeAxes, type ResizeBoxGeometry, type ResizeBoxResult, type ResizeHandle, type ResourceStatus, type ResourceStatusMap, type RowsPathCandidate, type RowsPathCandidateResult, type RowsPathPickerEntry, SNAP_THRESHOLD_SCREEN_PX, STYLE_FIELD_KEYS_BY_TYPE, type ShortcutCombo, type Size, type SnapAxis, type SnapAxisSource, type SnapBoxGeometry, type SnapPoints, type SnapResolution, type StyleSnapshot, TEXT_PRESETS, type TextEditLayout, type TextEditingSession, type TextPreset, type TextPresetId, type TransactOptions, type UploadFile, type UploadHandler, UploadHandlerMissingError, type UploadImagePlacement, type Viewport, WHEEL_ZOOM_STEP, type WheelInput, type WheelIntent, type ZoomBounds, addRootLayerInDraft, addTableCellInDraft, addTableRowInDraft, addTemplateCellInDraft, alignToCanvasTarget, applyStyleFieldsInDraft, buildLayerOutline, canCopyLayerAt, canonicalizeTableSyncInDraft, captureStyleSnapshot, clampZoom, classifyEditorShortcut, classifyWheel, cloneLayerSubtree, convertTableToRowsInDraft, convertTableToTemplateInDraft, createDefaultLayer, createRubberBandRect, createTemplateTable, defaultNextTextStyle, defaultSizeForRatio, deleteLayerInDraft, enumerateExpressionCandidates, enumerateRowsPathCandidates, enumerateRowsPathPickerEntries, expressionCallSlotAtEnd, expressionDiagnostics, expressionFragmentAtCursor, findPresetEntry, fitRect, fitViewport, growRowToCellInDraft, handleResizesHeight, handleResizesWidth, hitTest, insertRootLayerAdjacentInDraft, insertRootLayerInDraft, isCallFragment, isImageContentUndrawn, isLayerPath, isLockedPath, isRootLayerPath, isRootPasteableType, isTemplateSubtreePath, layerBoxByPath, memoizeTextPolicies, moveGuard, moveRootLayerInDraft, moveTableCellInDraft, moveTableCellToRowInDraft, moveTableRowInDraft, moveTableRowToTableInDraft, nextZoomByWheel, normalizeExpressionSchemaSource, panBy, panToBox, parseExpressionCall, parseExpressionPath, parseExpressionSchema, pastePosition, pathStartsWith, pathsEqual, phpTrim, pickNextTextStylePatch, prepareRootPaste, proportionalDrivingAxis, ratioValue, readSpecField, remapPathAfterSplice, replaceHitsInDraft, resizableAxesAt, resizeBox, resizeBoxProportional, resizeHandleAt, resizeHandlePoint, resizeHandlesAt, resizeProportionalSnapPoints, resizeSnapPoints, resolveLayer, resolveRowSchema, resolveRowsPathStartSchema, resolveSnap, resolveSnapPoints, resourcePathDiagnostics, rootLayerOf, scanExpressionFragments, scanTextMatches, sceneToScreen, schemaChildEntries, schemaNodeAtPath, screenToScene, selectionParentPath, setCellAutoHeightInDraft, snapAxesFromBoxes, snapThresholdScene, snapViewportToPhysicalPixels, syncContentIntoCellInDraft, syncRowWidthInDraft, uploadDisplayName, visibleRootBoxes, writeSpecField, zoomAtPoint };
package/dist/index.js CHANGED
@@ -1004,6 +1004,20 @@ function expressionFragmentAtCursor(scan, cursor) {
1004
1004
  return null;
1005
1005
  }
1006
1006
 
1007
+ // src/shared/resourcePath.ts
1008
+ function wholeTemplateDiagnostic(template, kind) {
1009
+ return { kind, start: 0, end: template.length, at: { start: 0, end: template.length } };
1010
+ }
1011
+ function resourcePathDiagnostics(template) {
1012
+ const anchored = template.endsWith("\n") ? template.slice(0, -1) : template;
1013
+ const matched = /^\{\{([^{}]*)\}\}$/.exec(anchored);
1014
+ if (matched === null) return [wholeTemplateDiagnostic(template, "resource_not_single_ref")];
1015
+ const path = phpTrim2(matched[1] ?? "");
1016
+ if (path === "") return [wholeTemplateDiagnostic(template, "empty_fragment")];
1017
+ if (path.includes("(")) return [wholeTemplateDiagnostic(template, "resource_function_call")];
1018
+ return [];
1019
+ }
1020
+
1007
1021
  // src/editing/alignCanvas.ts
1008
1022
  var ALIGN_CORNER_MARGIN_PX = 40;
1009
1023
  function alignToCanvasTarget(mode, box, canvasWidth, canvasHeight, margin) {
@@ -1748,6 +1762,7 @@ var EditorStore = class {
1748
1762
  anchorExpanded: false,
1749
1763
  nextTextStyle: defaultNextTextStyle(),
1750
1764
  dataSourceSchema: null,
1765
+ dataSourceSchemaRejection: null,
1751
1766
  guides: [],
1752
1767
  rulersVisible: true,
1753
1768
  snapAxes: [],
@@ -1940,12 +1955,13 @@ var EditorStore = class {
1940
1955
  }
1941
1956
  /**
1942
1957
  * 数据源 schema 声明替换(归一产物或降级 null,校验收口在注入缝不在这里):
1943
- * 同引用短路(重复注入同一归一产物不惊动订阅方);仅 ui 通知,不参与重绘
1958
+ * 声明与拒绝详情成对置换(rejection 仅被拒注入在场,2b 呈现面),同引用且
1959
+ * 同拒绝态短路(重复注入同一归一产物不惊动订阅方);仅 ui 通知,不参与重绘
1944
1960
  * 脏标(onStoreChange 对该分支短路)。
1945
1961
  */
1946
- setDataSourceSchema(schema) {
1947
- if (this.uiValue.dataSourceSchema === schema) return;
1948
- this.uiValue = { ...this.uiValue, dataSourceSchema: schema };
1962
+ setDataSourceSchema(schema, rejection = null) {
1963
+ if (this.uiValue.dataSourceSchema === schema && this.uiValue.dataSourceSchemaRejection === rejection) return;
1964
+ this.uiValue = { ...this.uiValue, dataSourceSchema: schema, dataSourceSchemaRejection: rejection };
1949
1965
  this.notify({ scope: "ui", branch: "dataSourceSchema" });
1950
1966
  }
1951
1967
  /** 参考线列表替换(增删动作在会话门面收口);仅 ui 通知,不参与历史 */
@@ -2301,6 +2317,17 @@ var ALT_DRAG_DEAD_ZONE_SCREEN_PX = 4;
2301
2317
  var NUDGE_STEP_PX = 1;
2302
2318
  var NUDGE_COARSE_STEP_PX = 10;
2303
2319
  var nudgeMergeKey = (path) => `nudge:${path.join(":")}`;
2320
+ function normalizeCanvasBackground(value) {
2321
+ if (value === null) return null;
2322
+ const trim = (raw) => typeof raw === "string" && raw.trim() !== "" ? raw : null;
2323
+ const color = trim(value.color);
2324
+ const image = trim(value.image);
2325
+ return color === null && image === null ? null : { color, image };
2326
+ }
2327
+ function sameCanvasBackground(a, b) {
2328
+ if (a === null || b === null) return a === b;
2329
+ return a.color === b.color && a.image === b.image;
2330
+ }
2304
2331
  function clampFindCursor(cursor, count) {
2305
2332
  if (count <= 0) return 0;
2306
2333
  return Math.min(Math.max(cursor, 0), count - 1);
@@ -3582,8 +3609,11 @@ var EditorSession = class {
3582
3609
  }
3583
3610
  /**
3584
3611
  * 数据字段取值方式切换(静态 ⇆ 表达式,工单 02 自面板收编内核):表达式现态
3585
- * → 字面接管(updateData,值保持现镜像);静态现态 → 打标(updateDataExpression,
3586
- * 初值 = 值字段原文)。各为独立一步历史(无 mergeKey)。三内容层分派各取值
3612
+ * → 字面接管(updateData,值保持现镜像);静态现态 → 打标,初值分内容面——
3613
+ * 文本层镜像原文(字面可含合法插值混排),资源层(Image/Qr)只镜像**已合法
3614
+ * 的单引用字面**(静态 ⇆ 表达式往返无损),违例字面(URL/拼接文案)落
3615
+ * 「未选择」空态不镜像——切换自身不得制造必违例初值(死端态整改条目 4;旧
3616
+ * 值可经撤销一步找回)。各为独立一步历史(无 mergeKey)。三内容层分派各取值
3587
3617
  * 字段,表/行/格/悬空路径空转。
3588
3618
  *
3589
3619
  * 活标志防御路由(ADR 0019):路径即编辑中的层时只翻会话活标志(零文档变更
@@ -3605,16 +3635,20 @@ var EditorSession = class {
3605
3635
  return;
3606
3636
  case "ImageLayer":
3607
3637
  if (layer.expression !== null) this.updateData(path, layer.src ?? "");
3608
- else this.updateDataExpression(path, layer.src ?? "");
3638
+ else this.updateDataExpression(path, this.resourceToggleInitial(layer.src ?? ""));
3609
3639
  return;
3610
3640
  case "QrCodeLayer":
3611
3641
  if (layer.expression !== null) this.updateData(path, layer.value);
3612
- else this.updateDataExpression(path, layer.value);
3642
+ else this.updateDataExpression(path, this.resourceToggleInitial(layer.value));
3613
3643
  return;
3614
3644
  default:
3615
3645
  return;
3616
3646
  }
3617
3647
  }
3648
+ /** 资源面切换初值:字面已是合法单引用(含空串为「未选择」)才镜像,违例落空态 */
3649
+ resourceToggleInitial(literal) {
3650
+ return resourcePathDiagnostics(literal).length === 0 ? literal : "";
3651
+ }
3618
3652
  /**
3619
3653
  * 内容写事务的统一形(会话内容独占工单 02):transact 落库 + 命中编辑层的
3620
3654
  * 草稿接管收口——updateData/updateDataExpression/replaceOne/replaceAll 四个
@@ -3637,9 +3671,15 @@ var EditorSession = class {
3637
3671
  if (!paths.some((path) => pathsEqual(path, editing.path))) return;
3638
3672
  this.store.setEditing({ ...editing, draftSeq: (editing.draftSeq ?? 0) + 1 });
3639
3673
  }
3640
- /** 更新画布级字段(未选中图层时面板的宽/高写入口);键由类型收窄为 width/height */
3641
3674
  updateCanvasProp(key, value, options = {}) {
3642
3675
  this.store.transact((draft) => {
3676
+ if (key === "background") {
3677
+ const next = normalizeCanvasBackground(value);
3678
+ const current = draft.background ?? null;
3679
+ if (sameCanvasBackground(current, next)) return;
3680
+ draft.background = next;
3681
+ return;
3682
+ }
3643
3683
  draft[key] = value;
3644
3684
  }, options);
3645
3685
  }
@@ -3658,9 +3698,20 @@ var EditorSession = class {
3658
3698
  * 换文档不重置,重注入/清除走同一入口。签名与「注入即整体替换」语义不变(D6)。
3659
3699
  */
3660
3700
  setDataSourceSchema(raw) {
3661
- this.store.setDataSourceSchema(
3662
- raw === null || raw === void 0 ? null : normalizeExpressionSchemaSource(raw)
3663
- );
3701
+ if (raw === null || raw === void 0) {
3702
+ this.store.setDataSourceSchema(null);
3703
+ return;
3704
+ }
3705
+ const schema = normalizeExpressionSchemaSource(raw);
3706
+ if (schema === null) {
3707
+ const parsed = parseExpressionSchema(raw);
3708
+ this.store.setDataSourceSchema(
3709
+ null,
3710
+ parsed.ok ? null : { reason: parsed.reason, detail: parsed.detail }
3711
+ );
3712
+ return;
3713
+ }
3714
+ this.store.setDataSourceSchema(schema);
3664
3715
  }
3665
3716
  // ---- 上传(工单 13):本机资源 → 可物化引用,实现经 uploadHandler 注入 ----
3666
3717
  /** 上传可用性:未注入 uploadHandler 时为 false(宿主据此隐藏/禁用上传入口) */
@@ -4526,20 +4577,6 @@ function expressionDiagnostics(template) {
4526
4577
  return found;
4527
4578
  }
4528
4579
 
4529
- // src/shared/resourcePath.ts
4530
- function wholeTemplateDiagnostic(template, kind) {
4531
- return { kind, start: 0, end: template.length, at: { start: 0, end: template.length } };
4532
- }
4533
- function resourcePathDiagnostics(template) {
4534
- const anchored = template.endsWith("\n") ? template.slice(0, -1) : template;
4535
- const matched = /^\{\{([^{}]*)\}\}$/.exec(anchored);
4536
- if (matched === null) return [wholeTemplateDiagnostic(template, "resource_not_single_ref")];
4537
- const path = phpTrim2(matched[1] ?? "");
4538
- if (path === "") return [wholeTemplateDiagnostic(template, "empty_fragment")];
4539
- if (path.includes("(")) return [wholeTemplateDiagnostic(template, "resource_function_call")];
4540
- return [];
4541
- }
4542
-
4543
4580
  // src/shared/expressionFunctions.ts
4544
4581
  var EXPRESSION_FUNCTIONS = [
4545
4582
  { name: "date", signature: "date(path[, format])", summary: "\u65E5\u671F\u683C\u5F0F\u5316\uFF08moment \u98CE\u683C token\uFF0C\u9884\u8BBE date/datetime\uFF09" },
@@ -4741,13 +4778,29 @@ function isArrayNode(node) {
4741
4778
  function isScalarNode(node) {
4742
4779
  return !isArrayNode(node) && node.properties === void 0;
4743
4780
  }
4781
+ function joinSegment(prefix, key) {
4782
+ return prefix === "" ? key : `${prefix}.${key}`;
4783
+ }
4784
+ function withMeta(target, node) {
4785
+ if (node.description !== void 0) target.description = node.description;
4786
+ if (node.title !== void 0) target.title = node.title;
4787
+ return target;
4788
+ }
4789
+ function descendToLayer(start, segments) {
4790
+ let base = start;
4791
+ for (const segment of segments) {
4792
+ if (isArrayNode(base)) return null;
4793
+ const child = schemaNodeAtPath(base, [segment]);
4794
+ if (child === null) return null;
4795
+ base = child;
4796
+ }
4797
+ return isArrayNode(base) || isScalarNode(base) ? null : base;
4798
+ }
4744
4799
  function childCandidate2(key, node, prefix) {
4745
- const candidate = {
4746
- path: prefix === "" ? key : `${prefix}.${key}`,
4747
- segment: key
4748
- };
4749
- if (node.description !== void 0) candidate.description = node.description;
4750
- if (node.title !== void 0) candidate.title = node.title;
4800
+ const candidate = withMeta(
4801
+ { path: joinSegment(prefix, key), segment: key },
4802
+ node
4803
+ );
4751
4804
  if (isArrayNode(node)) candidate.type = "array";
4752
4805
  return candidate;
4753
4806
  }
@@ -4766,17 +4819,40 @@ function enumerateRowsPathCandidates(startSchema, input) {
4766
4819
  const segments = input.split(".");
4767
4820
  const partial = input.endsWith(".") ? "" : segments[segments.length - 1];
4768
4821
  const completed = segments.slice(0, -1);
4769
- let base = startSchema;
4770
- for (const segment of completed) {
4771
- if (isArrayNode(base)) return null;
4772
- const child = schemaNodeAtPath(base, [segment]);
4773
- if (child === null) return null;
4774
- base = child;
4775
- }
4776
- if (isArrayNode(base) || isScalarNode(base)) return null;
4822
+ const base = descendToLayer(startSchema, completed);
4823
+ if (base === null) return null;
4777
4824
  const view = levelCandidates(base, completed.join("."));
4778
4825
  return { partial, candidates: view };
4779
4826
  }
4827
+ function leadsToArray(node) {
4828
+ if (isArrayNode(node)) return true;
4829
+ if (node.properties === void 0) return false;
4830
+ for (const child of node.properties.values()) {
4831
+ if (leadsToArray(child)) return true;
4832
+ }
4833
+ return false;
4834
+ }
4835
+ function pickerEntry(key, node, prefix) {
4836
+ return withMeta(
4837
+ { path: joinSegment(prefix, key), segment: key, isArray: isArrayNode(node) },
4838
+ node
4839
+ );
4840
+ }
4841
+ function enumerateRowsPathPickerEntries(startSchema, prefix) {
4842
+ if (startSchema === null) return [];
4843
+ const segments = prefix === "" ? [] : prefix.split(".");
4844
+ if (segments.includes("")) return [];
4845
+ const base = descendToLayer(startSchema, segments);
4846
+ if (base === null) return [];
4847
+ const entries = [];
4848
+ if (base.properties !== void 0) {
4849
+ for (const [key, child] of base.properties) {
4850
+ if (!leadsToArray(child)) continue;
4851
+ entries.push(pickerEntry(key, child, prefix));
4852
+ }
4853
+ }
4854
+ return entries;
4855
+ }
4780
4856
  function resolveRowsPathStartSchema(doc, path, rootSchema) {
4781
4857
  if (rootSchema === null) return null;
4782
4858
  if (doc === null || path === null) return rootSchema;
@@ -4929,6 +5005,7 @@ export {
4929
5005
  deleteLayerInDraft,
4930
5006
  enumerateExpressionCandidates,
4931
5007
  enumerateRowsPathCandidates,
5008
+ enumerateRowsPathPickerEntries,
4932
5009
  expressionCallSlotAtEnd,
4933
5010
  expressionDiagnostics,
4934
5011
  expressionFragmentAtCursor,