@skopon-cool/form-sdk 0.1.3 → 0.1.5

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.
Files changed (73) hide show
  1. package/README.md +2 -0
  2. package/dist/adapter/a2uiAdapter.d.ts +0 -1
  3. package/dist/adapter/a2uiAdapter.d.ts.map +1 -1
  4. package/dist/adapter/formSchema.d.ts.map +1 -1
  5. package/dist/blocks/case_multiselect/adapter.d.ts +10 -0
  6. package/dist/blocks/case_multiselect/adapter.d.ts.map +1 -0
  7. package/dist/blocks/case_multiselect/index.d.ts +3 -0
  8. package/dist/blocks/case_multiselect/index.d.ts.map +1 -0
  9. package/dist/blocks/case_singleselect/catalog.d.ts +2 -0
  10. package/dist/blocks/case_singleselect/catalog.d.ts.map +1 -0
  11. package/dist/blocks/registry.d.ts +8 -0
  12. package/dist/blocks/registry.d.ts.map +1 -0
  13. package/dist/blocks/resume_multiselect/adapter.d.ts +10 -0
  14. package/dist/blocks/resume_multiselect/adapter.d.ts.map +1 -0
  15. package/dist/blocks/resume_multiselect/catalog.d.ts +2 -0
  16. package/dist/blocks/resume_multiselect/catalog.d.ts.map +1 -0
  17. package/dist/blocks/resume_multiselect/index.d.ts +3 -0
  18. package/dist/blocks/resume_multiselect/index.d.ts.map +1 -0
  19. package/dist/blocks/types.d.ts +27 -0
  20. package/dist/blocks/types.d.ts.map +1 -0
  21. package/dist/catalog/a2uiCustomCatalog.d.ts.map +1 -1
  22. package/dist/catalog/caseSearchContext.d.ts +28 -0
  23. package/dist/catalog/caseSearchContext.d.ts.map +1 -0
  24. package/dist/catalog/resumeSearchContext.d.ts +39 -0
  25. package/dist/catalog/resumeSearchContext.d.ts.map +1 -0
  26. package/dist/catalog/skoponCaseSelect.d.ts +2 -0
  27. package/dist/catalog/skoponCaseSelect.d.ts.map +1 -0
  28. package/dist/catalog/skoponResumeSelect.d.ts +2 -0
  29. package/dist/catalog/skoponResumeSelect.d.ts.map +1 -0
  30. package/dist/components/AskUserFormCard.d.ts +7 -1
  31. package/dist/components/AskUserFormCard.d.ts.map +1 -1
  32. package/dist/components/SkoponA2uiStreamRenderer.d.ts +7 -1
  33. package/dist/components/SkoponA2uiStreamRenderer.d.ts.map +1 -1
  34. package/dist/components/SkoponFormRenderer.d.ts +6 -0
  35. package/dist/components/SkoponFormRenderer.d.ts.map +1 -1
  36. package/dist/form-sdk.css +1 -1
  37. package/dist/index.d.ts +5 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +1374 -889
  40. package/dist/submit/buildCurlStatement.d.ts +1 -1
  41. package/dist/submit/buildCurlStatement.d.ts.map +1 -1
  42. package/dist/submit/intersectPayloadBlocksWithForm.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +33 -2
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/src/adapter/a2uiAdapter.test.ts +71 -0
  47. package/src/adapter/a2uiAdapter.ts +41 -4
  48. package/src/adapter/formSchema.ts +102 -19
  49. package/src/blocks/case_multiselect/adapter.ts +90 -0
  50. package/src/blocks/case_multiselect/index.ts +14 -0
  51. package/src/blocks/case_singleselect/catalog.ts +1 -0
  52. package/src/blocks/registry.ts +34 -0
  53. package/src/blocks/resume_multiselect/adapter.ts +57 -0
  54. package/src/blocks/resume_multiselect/catalog.ts +1 -0
  55. package/src/blocks/resume_multiselect/index.ts +14 -0
  56. package/src/blocks/types.ts +34 -0
  57. package/src/catalog/a2uiCustomCatalog.tsx +6 -0
  58. package/src/catalog/caseSearchContext.tsx +46 -0
  59. package/src/catalog/resumeSearchContext.tsx +58 -0
  60. package/src/catalog/skoponCaseSelect.tsx +240 -0
  61. package/src/catalog/skoponResumeSelect.tsx +293 -0
  62. package/src/components/AskUserFormCard.tsx +13 -1
  63. package/src/components/SkoponA2uiStreamRenderer.test.ts +18 -2
  64. package/src/components/SkoponA2uiStreamRenderer.test.tsx +26 -2
  65. package/src/components/SkoponA2uiStreamRenderer.tsx +71 -27
  66. package/src/components/SkoponFormRenderer.tsx +32 -10
  67. package/src/index.ts +23 -0
  68. package/src/styles/a2ui-preview.css +4 -4
  69. package/src/styles/index.css +191 -0
  70. package/src/submit/buildCurlStatement.ts +2 -23
  71. package/src/submit/intersectPayloadBlocksWithForm.ts +14 -2
  72. package/src/submit/submit.test.ts +34 -3
  73. package/src/types/index.ts +37 -0
@@ -1,5 +1,5 @@
1
1
  export declare function buildCurlStatement(payload: unknown, callbackUrl?: string | null): string;
2
- /** 构建含行注释的 JSON bodyextra 段标注「额外字段(未在卡片展示)」 */
2
+ /** 构建合法 JSON body(card + extra 合并,便于 curl 直接执行) */
3
3
  export declare function buildAskUserCurlBodyJson(cardValues: Record<string, unknown>, extraValues: Record<string, unknown>): string;
4
4
  export interface BuildAskUserCurlStatementOptions {
5
5
  cardValues: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"buildCurlStatement.d.ts","sourceRoot":"","sources":["../../src/submit/buildCurlStatement.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAQxF;AAMD,gDAAgD;AAChD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAmBR;AAED,MAAM,WAAW,gCAAgC;IAC/C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,wBAAgB,yBAAyB,CAAC,EACxC,UAAU,EACV,WAAgB,EAChB,WAAW,GACZ,EAAE,gCAAgC,GAAG,MAAM,CAQ3C"}
1
+ {"version":3,"file":"buildCurlStatement.d.ts","sourceRoot":"","sources":["../../src/submit/buildCurlStatement.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAQxF;AAED,mDAAmD;AACnD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,MAAM,CAER;AAED,MAAM,WAAW,gCAAgC;IAC/C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAED,wBAAgB,yBAAyB,CAAC,EACxC,UAAU,EACV,WAAgB,EAChB,WAAW,GACZ,EAAE,gCAAgC,GAAG,MAAM,CAQ3C"}
@@ -1 +1 @@
1
- {"version":3,"file":"intersectPayloadBlocksWithForm.d.ts","sourceRoot":"","sources":["../../src/submit/intersectPayloadBlocksWithForm.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3D,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,CAAC,CAAA;AAE1F,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,0DAA0D;IAC1D,YAAY,EAAE,SAAS,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAqDD,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI,CAUvF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,uBAAuB,EACnC,cAAc,EAAE,UAAU,GAAG,SAAS,GACrC,yBAAyB,CAqC3B;AAmBD,2CAA2C;AAC3C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzF;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,uBAAuB,GAAG,OAAO,CAIlF;AAED,0DAA0D;AAC1D,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,uBAAuB,GAAG,SAAS,EAAE,CAK3F;AAED,mDAAmD;AACnD,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,uBAAuB,GAAG,MAAM,EAAE,CAIvF"}
1
+ {"version":3,"file":"intersectPayloadBlocksWithForm.d.ts","sourceRoot":"","sources":["../../src/submit/intersectPayloadBlocksWithForm.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3D,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,CAAC,CAAA;AAE1F,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,0DAA0D;IAC1D,YAAY,EAAE,SAAS,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AAqDD,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI,CAUvF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,uBAAuB,EACnC,cAAc,EAAE,UAAU,GAAG,SAAS,GACrC,yBAAyB,CAqC3B;AA+BD,2CAA2C;AAC3C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzF;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,uBAAuB,GAAG,OAAO,CAIlF;AAED,0DAA0D;AAC1D,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,uBAAuB,GAAG,SAAS,EAAE,CAK3F;AAED,mDAAmD;AACnD,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,uBAAuB,GAAG,MAAM,EAAE,CAIvF"}
@@ -4,7 +4,7 @@
4
4
  export type A2uiBinding<T> = T | {
5
5
  path: string;
6
6
  };
7
- export type A2uiComponentName = 'Column' | 'Row' | 'Text' | 'Image' | 'Video' | 'AudioPlayer' | 'TextField' | 'CheckBox' | 'ChoicePicker' | 'DateTimeInput' | 'FileUpload' | 'SkoponMedia' | 'SkoponSelect';
7
+ export type A2uiComponentName = 'Column' | 'Row' | 'Text' | 'Image' | 'Video' | 'AudioPlayer' | 'TextField' | 'CheckBox' | 'ChoicePicker' | 'DateTimeInput' | 'FileUpload' | 'SkoponMedia' | 'SkoponSelect' | 'SkoponResumeSelect' | 'SkoponCaseSelect';
8
8
  export interface A2uiComponentNode {
9
9
  id: string;
10
10
  component: A2uiComponentName | string;
@@ -21,7 +21,9 @@ export interface A2uiSurfaceDoc {
21
21
  }
22
22
  export declare const A2UI_PROTOCOL_VERSION: "v0.9";
23
23
  export type FormJsonSchema = Record<string, unknown>;
24
- export type FormBlockType = 'heading' | 'paragraph' | 'text' | 'textarea' | 'email' | 'number' | 'select' | 'multiselect' | 'radio' | 'checkbox' | 'toggle' | 'tel' | 'url' | 'datetime' | 'time' | 'file' | 'image' | 'video' | 'audio';
24
+ export type FormBlockType = 'heading' | 'paragraph' | 'text' | 'textarea' | 'email' | 'number' | 'select' | 'multiselect' | 'radio' | 'checkbox' | 'toggle' | 'tel' | 'url' | 'datetime' | 'time' | 'file' | 'resume_multiselect' | 'case_multiselect'
25
+ /** @deprecated 加载时自动迁移为 case_multiselect */
26
+ | 'case_singleselect' | 'image' | 'video' | 'audio';
25
27
  export type FormMediaSize = 'huge' | 'large' | 'medium' | 'small' | 'icon';
26
28
  export type FormFilePlaceholderIcon = 'video' | 'audio' | 'image' | 'file' | 'spreadsheet' | 'document';
27
29
  export declare const FORM_MEDIA_SIZES: FormMediaSize[];
@@ -29,6 +31,27 @@ export interface FormBlockOption {
29
31
  value: string;
30
32
  label: string;
31
33
  }
34
+ /** 简历多选组件:渲染时用于 /univ/resume/search 的筛选条件 */
35
+ export interface FormResumeFilter {
36
+ names?: string[];
37
+ agentUniqueIds?: string[];
38
+ resumeUniqueIds?: string[];
39
+ pageSize?: number;
40
+ }
41
+ /** 案例多选组件:渲染时用于 /univ/case/list 的筛选条件 */
42
+ export interface FormCaseFilter {
43
+ /** Agent 类型(编辑器辅助,运行时不用) */
44
+ agentKind?: string;
45
+ /** Agent unique_id(运行时用于限定所属 Agent) */
46
+ agentUniqueId?: string;
47
+ /** 流类型 unique_id */
48
+ flowType?: string;
49
+ /** 流类别 unique_id */
50
+ category?: string;
51
+ /** 流程编号(flow_unique_id)多选 */
52
+ flowUniqueIds?: string[];
53
+ pageSize?: number;
54
+ }
32
55
  export interface FormBlock {
33
56
  id: string;
34
57
  type: FormBlockType;
@@ -47,6 +70,14 @@ export interface FormBlock {
47
70
  fileMinCount?: number;
48
71
  fileMaxCount?: number;
49
72
  defaultValue?: string | string[] | boolean;
73
+ /** 简历多选:是否显示「换一批」 */
74
+ resumeEnableRefresh?: boolean;
75
+ /** 简历多选:列表筛选条件(持久化进 block json) */
76
+ resumeFilter?: FormResumeFilter;
77
+ /** 案例单选:是否显示「换一批」 */
78
+ caseEnableRefresh?: boolean;
79
+ /** 案例单选:列表筛选条件(持久化进 block json) */
80
+ caseFilter?: FormCaseFilter;
50
81
  }
51
82
  export interface FormSchema {
52
83
  title?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.3",
6
+ "version": "0.1.5",
7
7
  "description": "Skopon form rendering SDK (A2UI + form_definition) with submit helpers",
8
8
  "type": "module",
9
9
  "sideEffects": [
@@ -4,6 +4,7 @@
4
4
  import { describe, it, expect } from 'vitest'
5
5
  import type { FormSchema } from '../types/index'
6
6
  import { a2uiToBlocks, blocksToA2ui, isA2uiSurfaceEmpty, surfaceDocToMessages } from './a2uiAdapter'
7
+ import { normalizeFormDefinition } from './formSchema'
7
8
 
8
9
  function makeDefinition(): Pick<FormSchema, 'title' | 'description' | 'blocks'> {
9
10
  return {
@@ -238,4 +239,74 @@ describe('a2uiAdapter blocks <-> surface', () => {
238
239
  const withHeader = blocksToA2ui(makeDefinition())
239
240
  expect((withHeader.components.find((c) => c.id === 'root')?.children as string[]).length).toBe(12)
240
241
  })
242
+
243
+ it('round-trips case_multiselect through blocksToA2ui and a2uiToBlocks', () => {
244
+ const doc = blocksToA2ui(
245
+ {
246
+ title: '',
247
+ description: '',
248
+ blocks: [
249
+ {
250
+ id: 'b-case',
251
+ type: 'case_multiselect',
252
+ name: 'selected_cases',
253
+ label: '选择案例',
254
+ placeholder: '暂无案例',
255
+ help: '请选择一个或多个案例',
256
+ caseEnableRefresh: true,
257
+ caseFilter: {
258
+ agentKind: 'public-user',
259
+ agentUniqueId: 'agent-1',
260
+ flowType: 'content',
261
+ category: 'short-video',
262
+ flowUniqueIds: ['flow-rec-a', 'flow-rec-b'],
263
+ pageSize: 10,
264
+ },
265
+ defaultValue: ['case-abc', 'case-def'],
266
+ },
267
+ ],
268
+ },
269
+ { includeHeader: false },
270
+ )
271
+ const node = doc.components.find((c) => c.id === 'b-case')
272
+ expect(node?.component).toBe('SkoponCaseSelect')
273
+ expect(node?.enableRefresh).toBe(true)
274
+ expect(node?.caseFilter).toEqual({
275
+ agentKind: 'public-user',
276
+ agentUniqueId: 'agent-1',
277
+ flowType: 'content',
278
+ category: 'short-video',
279
+ flowUniqueIds: ['flow-rec-a', 'flow-rec-b'],
280
+ pageSize: 10,
281
+ })
282
+ expect(doc.dataModel?.selected_cases).toEqual(['case-abc', 'case-def'])
283
+
284
+ const back = a2uiToBlocks(doc)
285
+ const block = back.blocks.find((b) => b.name === 'selected_cases')
286
+ expect(block?.type).toBe('case_multiselect')
287
+ expect(block?.caseEnableRefresh).toBe(true)
288
+ expect(block?.caseFilter?.flowType).toBe('content')
289
+ expect(block?.caseFilter?.category).toBe('short-video')
290
+ expect(block?.caseFilter?.flowUniqueIds).toEqual(['flow-rec-a', 'flow-rec-b'])
291
+ expect(block?.caseFilter?.pageSize).toBe(10)
292
+ expect(block?.defaultValue).toEqual(['case-abc', 'case-def'])
293
+ })
294
+
295
+ it('migrates legacy case_singleselect to case_multiselect on normalize', () => {
296
+ const normalized = normalizeFormDefinition({
297
+ title: '',
298
+ description: '',
299
+ blocks: [
300
+ {
301
+ id: 'legacy',
302
+ type: 'case_singleselect',
303
+ name: 'old_case',
304
+ defaultValue: 'case-old',
305
+ },
306
+ ],
307
+ jsonSchema: {},
308
+ })
309
+ expect(normalized.blocks[0]?.type).toBe('case_multiselect')
310
+ expect(normalized.blocks[0]?.defaultValue).toEqual(['case-old'])
311
+ })
241
312
  })
@@ -11,6 +11,7 @@ import { generateId } from './id'
11
11
  import { coerceToggleValue } from './formSchema'
12
12
  import { getMediaUrls, normalizeMediaSize } from './formMedia'
13
13
  import { syncFormDefinition } from './formSchema'
14
+ import { getBlockTypeAdapter, getComponentAdapter } from '../blocks/registry'
14
15
 
15
16
  /**
16
17
  * A2UI surface <-> FormBlock 双向适配器。
@@ -99,14 +100,17 @@ function defaultValueForBlock(block: FormBlock): unknown {
99
100
  return coerceToggleValue(block.defaultValue)
100
101
  case 'multiselect':
101
102
  case 'checkbox':
103
+ case 'resume_multiselect':
104
+ case 'case_multiselect':
102
105
  if (Array.isArray(block.defaultValue)) return block.defaultValue
103
106
  if (typeof block.defaultValue === 'string' && block.defaultValue) return [block.defaultValue]
104
107
  return []
105
108
  case 'select':
106
109
  case 'radio':
107
- if (Array.isArray(block.defaultValue)) return block.defaultValue[0] ?? ''
108
- if (typeof block.defaultValue === 'string') return block.defaultValue
109
- return ''
110
+ case 'number':
111
+ if (typeof block.defaultValue === 'number') return block.defaultValue
112
+ if (typeof block.defaultValue === 'string' && block.defaultValue) return block.defaultValue
113
+ return null
110
114
  default:
111
115
  if (typeof block.defaultValue === 'string') return block.defaultValue
112
116
  return ''
@@ -131,6 +135,12 @@ function blockToComponent(block: FormBlock): {
131
135
  dataValue?: unknown
132
136
  } => (name ? { node, dataKey: name, dataValue: defaultValueForBlock(block) } : { node })
133
137
 
138
+ const adapterCtx = { id, label, name, path, withData }
139
+ const typeAdapter = getBlockTypeAdapter(block.type)
140
+ if (typeAdapter) {
141
+ return typeAdapter.toComponent(block, adapterCtx)
142
+ }
143
+
134
144
  switch (block.type) {
135
145
  case 'heading':
136
146
  return { node: { id, component: 'Text', text: label, variant: 'h3' } }
@@ -351,6 +361,11 @@ function componentToBlock(node: A2uiComponentNode): FormBlock | null {
351
361
 
352
362
  const base = (type: FormBlockType): FormBlock => ({ id, type, label })
353
363
 
364
+ const componentAdapter = getComponentAdapter(component)
365
+ if (componentAdapter) {
366
+ return componentAdapter.fromComponent(node, base, { asLiteral, nameFromValue })
367
+ }
368
+
354
369
  switch (component) {
355
370
  case 'Text': {
356
371
  const variant = String(node.variant ?? 'body')
@@ -465,6 +480,23 @@ function collectOrder(
465
480
  }
466
481
 
467
482
  /** A2UI surface 文档 -> FormSchema(已 sync 出 jsonSchema) */
483
+ function applyDataModelDefaults(
484
+ blocks: FormBlock[],
485
+ dataModel: Record<string, unknown> | undefined,
486
+ ): FormBlock[] {
487
+ if (!dataModel) return blocks
488
+ return blocks.map((block) => {
489
+ const name = block.name?.trim()
490
+ if (!name || !(name in dataModel)) return block
491
+ const raw = dataModel[name]
492
+ if (raw === undefined) return block
493
+ if (block.type === 'toggle') {
494
+ return { ...block, defaultValue: raw === true || raw === 'true' }
495
+ }
496
+ return { ...block, defaultValue: raw as FormBlock['defaultValue'] }
497
+ })
498
+ }
499
+
468
500
  export function a2uiToBlocks(doc: A2uiSurfaceDoc | null | undefined): FormSchema {
469
501
  if (!doc || !Array.isArray(doc.components)) {
470
502
  return syncFormDefinition({ title: '', description: '', blocks: [], jsonSchema: {} })
@@ -490,7 +522,12 @@ export function a2uiToBlocks(doc: A2uiSurfaceDoc | null | undefined): FormSchema
490
522
  if (block) blocks.push(block)
491
523
  }
492
524
 
493
- return syncFormDefinition({ title, description, blocks, jsonSchema: {} })
525
+ return syncFormDefinition({
526
+ title,
527
+ description,
528
+ blocks: applyDataModelDefaults(blocks, doc.dataModel),
529
+ jsonSchema: {},
530
+ })
494
531
  }
495
532
 
496
533
  /**
@@ -1,4 +1,4 @@
1
- import type { FormBlock, FormBlockType, FormSchema } from '../types/index'
1
+ import type { FormBlock, FormBlockType, FormCaseFilter, FormResumeFilter, FormSchema } from '../types/index'
2
2
  import { isInputBlockType, isMediaBlockType } from '../types/index'
3
3
  import { generateId } from './id'
4
4
  import {
@@ -29,7 +29,7 @@ function normalizeDefaultValue(
29
29
  ): FormBlock['defaultValue'] {
30
30
  if (raw === undefined) return undefined
31
31
  if (type === 'toggle') return coerceToggleValue(raw)
32
- if (type === 'multiselect' || type === 'checkbox') {
32
+ if (type === 'multiselect' || type === 'checkbox' || type === 'resume_multiselect' || type === 'case_multiselect') {
33
33
  if (Array.isArray(raw)) return raw.map(String)
34
34
  if (typeof raw === 'string' && raw) return [raw]
35
35
  return []
@@ -39,36 +39,102 @@ function normalizeDefaultValue(
39
39
  return String(raw)
40
40
  }
41
41
 
42
+ function normalizeCaseFilter(raw: FormCaseFilter | undefined): FormCaseFilter {
43
+ const pageSizeRaw = typeof raw?.pageSize === 'number' ? raw.pageSize : Number(raw?.pageSize)
44
+ const pageSize =
45
+ Number.isFinite(pageSizeRaw) && pageSizeRaw >= 1 && pageSizeRaw <= 100
46
+ ? Math.floor(pageSizeRaw)
47
+ : 20
48
+ const agentKind =
49
+ typeof raw?.agentKind === 'string' && raw.agentKind.trim()
50
+ ? raw.agentKind.trim()
51
+ : undefined
52
+ const agentUniqueId =
53
+ typeof raw?.agentUniqueId === 'string' && raw.agentUniqueId.trim()
54
+ ? raw.agentUniqueId.trim()
55
+ : undefined
56
+ const flowType =
57
+ typeof raw?.flowType === 'string' && raw.flowType.trim()
58
+ ? raw.flowType.trim()
59
+ : undefined
60
+ const category =
61
+ typeof raw?.category === 'string' && raw.category.trim()
62
+ ? raw.category.trim()
63
+ : undefined
64
+ const flowUniqueIds = Array.isArray(raw?.flowUniqueIds)
65
+ ? raw.flowUniqueIds.map(String).filter(Boolean)
66
+ : []
67
+ return { agentKind, agentUniqueId, flowType, category, flowUniqueIds, pageSize }
68
+ }
69
+
70
+ function normalizeResumeFilter(raw: FormResumeFilter | undefined): FormResumeFilter {
71
+ const pageSizeRaw = typeof raw?.pageSize === 'number' ? raw.pageSize : Number(raw?.pageSize)
72
+ const pageSize =
73
+ Number.isFinite(pageSizeRaw) && pageSizeRaw >= 1 && pageSizeRaw <= 100
74
+ ? Math.floor(pageSizeRaw)
75
+ : 20
76
+ return {
77
+ names: Array.isArray(raw?.names) ? raw!.names.map(String).filter(Boolean) : [],
78
+ agentUniqueIds: Array.isArray(raw?.agentUniqueIds)
79
+ ? raw!.agentUniqueIds.map(String).filter(Boolean)
80
+ : [],
81
+ resumeUniqueIds: Array.isArray(raw?.resumeUniqueIds)
82
+ ? raw!.resumeUniqueIds.map(String).filter(Boolean)
83
+ : [],
84
+ pageSize,
85
+ }
86
+ }
87
+
88
+ function migrateLegacyBlock(raw: FormBlock): FormBlock {
89
+ if (raw.type !== 'case_singleselect') return raw
90
+ const defaultValue =
91
+ typeof raw.defaultValue === 'string' && raw.defaultValue.trim()
92
+ ? [raw.defaultValue.trim()]
93
+ : Array.isArray(raw.defaultValue)
94
+ ? raw.defaultValue.map(String).filter(Boolean)
95
+ : []
96
+ return { ...raw, type: 'case_multiselect', defaultValue }
97
+ }
98
+
42
99
  function normalizeBlock(raw: FormBlock): FormBlock {
43
- const fileAcceptTypes = normalizeFileAcceptTypes(raw.fileAcceptTypes)
44
- const type = raw.type
100
+ const migrated = migrateLegacyBlock(raw)
101
+ const fileAcceptTypes = normalizeFileAcceptTypes(migrated.fileAcceptTypes)
102
+ const type = migrated.type
45
103
  return {
46
- id: raw.id?.trim() || generateId('block-'),
104
+ id: migrated.id?.trim() || generateId('block-'),
47
105
  type,
48
- name: raw.name?.trim() || undefined,
49
- label: raw.label ?? '',
50
- placeholder: raw.placeholder ?? '',
51
- help: raw.help ?? '',
52
- required: raw.required === true,
106
+ name: migrated.name?.trim() || undefined,
107
+ label: migrated.label ?? '',
108
+ placeholder: migrated.placeholder ?? '',
109
+ help: migrated.help ?? '',
110
+ required: migrated.required === true,
53
111
  mediaUrls: isMediaBlockType(type)
54
- ? normalizeMediaUrls(raw, DEFAULT_MEDIA_URL_MAX)
112
+ ? normalizeMediaUrls(migrated, DEFAULT_MEDIA_URL_MAX)
55
113
  : undefined,
56
- mediaSize: isMediaBlockType(type) ? normalizeMediaSize(raw.mediaSize) : undefined,
114
+ mediaSize: isMediaBlockType(type) ? normalizeMediaSize(migrated.mediaSize) : undefined,
57
115
  fileAcceptTypes,
58
116
  fileAcceptExtensions: filterFileAcceptExtensionsForTypes(
59
117
  fileAcceptTypes,
60
- raw.fileAcceptExtensions,
118
+ migrated.fileAcceptExtensions,
61
119
  ),
62
- filePlaceholderIcon: normalizeFilePlaceholderIcon(raw.filePlaceholderIcon),
63
- fileMinCount: type === 'file' ? normalizeFileUploadCount(raw.fileMinCount, 1) : undefined,
64
- fileMaxCount: type === 'file' ? normalizeFileUploadCount(raw.fileMaxCount, 1) : undefined,
65
- defaultValue: normalizeDefaultValue(type, raw.defaultValue),
66
- options: Array.isArray(raw.options)
67
- ? raw.options.map((opt, index) => ({
120
+ filePlaceholderIcon: normalizeFilePlaceholderIcon(migrated.filePlaceholderIcon),
121
+ fileMinCount: type === 'file' ? normalizeFileUploadCount(migrated.fileMinCount, 1) : undefined,
122
+ fileMaxCount: type === 'file' ? normalizeFileUploadCount(migrated.fileMaxCount, 1) : undefined,
123
+ defaultValue: normalizeDefaultValue(type, migrated.defaultValue),
124
+ options: Array.isArray(migrated.options)
125
+ ? migrated.options.map((opt, index) => ({
68
126
  value: opt.value?.trim() || `option_${index + 1}`,
69
127
  label: opt.label?.trim() || `选项 ${index + 1}`,
70
128
  }))
71
129
  : undefined,
130
+ resumeEnableRefresh:
131
+ type === 'resume_multiselect' ? migrated.resumeEnableRefresh !== false : undefined,
132
+ resumeFilter:
133
+ type === 'resume_multiselect' ? normalizeResumeFilter(migrated.resumeFilter) : undefined,
134
+ caseEnableRefresh:
135
+ type === 'case_multiselect' ? migrated.caseEnableRefresh !== false : undefined,
136
+ caseFilter:
137
+ type === 'case_multiselect' ? normalizeCaseFilter(migrated.caseFilter) : undefined,
72
138
  }
73
139
  }
74
140
 
@@ -135,6 +201,23 @@ export function createEmptyBlock(type: FormBlockType): FormBlock {
135
201
  block.fileMaxCount = 1
136
202
  }
137
203
 
204
+ if (type === 'resume_multiselect') {
205
+ block.resumeEnableRefresh = true
206
+ block.resumeFilter = {
207
+ names: [],
208
+ agentUniqueIds: [],
209
+ resumeUniqueIds: [],
210
+ pageSize: 20,
211
+ }
212
+ block.defaultValue = []
213
+ }
214
+
215
+ if (type === 'case_multiselect') {
216
+ block.caseEnableRefresh = true
217
+ block.caseFilter = { flowUniqueIds: [], pageSize: 20 }
218
+ block.defaultValue = []
219
+ }
220
+
138
221
  return block
139
222
  }
140
223
 
@@ -0,0 +1,90 @@
1
+ import type { A2uiComponentNode, FormBlock } from '../../types/index'
2
+ import type { BlockAdapterContext, BlockToComponentResult } from '../types'
3
+
4
+ function asLiteral(value: unknown): string | undefined {
5
+ return typeof value === 'string' ? value : undefined
6
+ }
7
+
8
+ function readCaseFilterFromNode(cf: Record<string, unknown>) {
9
+ const pageSizeRaw = typeof cf.pageSize === 'number' ? cf.pageSize : Number(cf.pageSize)
10
+ const agentKind =
11
+ typeof cf.agentKind === 'string' && cf.agentKind.trim()
12
+ ? cf.agentKind.trim()
13
+ : undefined
14
+ const agentUniqueId =
15
+ typeof cf.agentUniqueId === 'string' && cf.agentUniqueId.trim()
16
+ ? cf.agentUniqueId.trim()
17
+ : undefined
18
+ const flowType =
19
+ typeof cf.flowType === 'string' && cf.flowType.trim()
20
+ ? cf.flowType.trim()
21
+ : undefined
22
+ const category =
23
+ typeof cf.category === 'string' && cf.category.trim()
24
+ ? cf.category.trim()
25
+ : undefined
26
+ const flowUniqueIds = Array.isArray(cf.flowUniqueIds)
27
+ ? cf.flowUniqueIds.map(String).filter(Boolean)
28
+ : []
29
+ return {
30
+ agentKind,
31
+ agentUniqueId,
32
+ flowType,
33
+ category,
34
+ flowUniqueIds,
35
+ pageSize:
36
+ Number.isFinite(pageSizeRaw) && pageSizeRaw >= 1 && pageSizeRaw <= 100
37
+ ? Math.floor(pageSizeRaw)
38
+ : 20,
39
+ }
40
+ }
41
+
42
+ export function caseMultiselectToComponent(
43
+ block: FormBlock,
44
+ ctx: BlockAdapterContext,
45
+ ): BlockToComponentResult {
46
+ const { id, label, path, withData } = ctx
47
+ return withData({
48
+ id,
49
+ component: 'SkoponCaseSelect',
50
+ label,
51
+ placeholder: block.placeholder ?? '',
52
+ help: block.help ?? '',
53
+ enableRefresh: block.caseEnableRefresh !== false,
54
+ caseFilter: {
55
+ agentKind: block.caseFilter?.agentKind,
56
+ agentUniqueId: block.caseFilter?.agentUniqueId,
57
+ flowType: block.caseFilter?.flowType,
58
+ category: block.caseFilter?.category,
59
+ flowUniqueIds: block.caseFilter?.flowUniqueIds ?? [],
60
+ pageSize: block.caseFilter?.pageSize ?? 20,
61
+ },
62
+ value: { path },
63
+ })
64
+ }
65
+
66
+ export function caseMultiselectFromComponent(
67
+ node: A2uiComponentNode,
68
+ base: (type: 'case_multiselect') => FormBlock,
69
+ helpers: {
70
+ asLiteral: (value: unknown) => string | undefined
71
+ nameFromValue: (value: unknown) => string
72
+ },
73
+ ): FormBlock {
74
+ const name = helpers.nameFromValue(node.value)
75
+ const block: FormBlock = {
76
+ ...base('case_multiselect'),
77
+ name,
78
+ caseEnableRefresh: node.enableRefresh !== false,
79
+ }
80
+ const placeholder = helpers.asLiteral(node.placeholder)
81
+ if (placeholder) block.placeholder = placeholder
82
+ const help = helpers.asLiteral(node.help)
83
+ if (help) block.help = help
84
+ if (node.caseFilter && typeof node.caseFilter === 'object') {
85
+ block.caseFilter = readCaseFilterFromNode(node.caseFilter as Record<string, unknown>)
86
+ }
87
+ return block
88
+ }
89
+
90
+ export { asLiteral }
@@ -0,0 +1,14 @@
1
+ import type { BlockAdapterPlugin } from '../types'
2
+ import {
3
+ caseMultiselectFromComponent,
4
+ caseMultiselectToComponent,
5
+ } from './adapter'
6
+ import { SkoponCaseSelectImpl } from '../../catalog/skoponCaseSelect'
7
+
8
+ export const caseMultiselectAdapter: BlockAdapterPlugin = {
9
+ type: 'case_multiselect',
10
+ componentName: 'SkoponCaseSelect',
11
+ toComponent: caseMultiselectToComponent,
12
+ fromComponent: caseMultiselectFromComponent,
13
+ catalogComponents: [SkoponCaseSelectImpl],
14
+ }
@@ -0,0 +1 @@
1
+ export { SkoponCaseSelectImpl } from '../../catalog/skoponCaseSelect'
@@ -0,0 +1,34 @@
1
+ import type { FormBlockType } from '../types/index'
2
+ import type { BlockAdapterPlugin } from './types'
3
+ import { caseMultiselectAdapter } from './case_multiselect'
4
+ import { resumeMultiselectAdapter } from './resume_multiselect'
5
+
6
+ const adapters: BlockAdapterPlugin[] = [resumeMultiselectAdapter, caseMultiselectAdapter]
7
+
8
+ const adapterByType = new Map<FormBlockType, BlockAdapterPlugin>(
9
+ adapters.map((adapter) => [adapter.type, adapter]),
10
+ )
11
+
12
+ const adapterByComponent = new Map<string, BlockAdapterPlugin>(
13
+ adapters.map((adapter) => [adapter.componentName, adapter]),
14
+ )
15
+
16
+ export function getBlockTypeAdapter(type: FormBlockType): BlockAdapterPlugin | undefined {
17
+ return adapterByType.get(type)
18
+ }
19
+
20
+ export function getComponentAdapter(componentName: string): BlockAdapterPlugin | undefined {
21
+ return adapterByComponent.get(componentName)
22
+ }
23
+
24
+ export function getAllBlockAdapters(): BlockAdapterPlugin[] {
25
+ return adapters
26
+ }
27
+
28
+ export function getRegisteredCatalogComponents() {
29
+ return adapters.flatMap((adapter) => adapter.catalogComponents)
30
+ }
31
+
32
+ export function getRegisteredCatalogComponentNames(): string[] {
33
+ return adapters.map((adapter) => adapter.componentName)
34
+ }
@@ -0,0 +1,57 @@
1
+ import type { A2uiComponentNode, FormBlock } from '../../types/index'
2
+ import type { BlockAdapterContext, BlockToComponentResult } from '../types'
3
+
4
+ function asLiteral(value: unknown): string | undefined {
5
+ return typeof value === 'string' ? value : undefined
6
+ }
7
+
8
+ export function resumeMultiselectToComponent(
9
+ block: FormBlock,
10
+ ctx: BlockAdapterContext,
11
+ ): BlockToComponentResult {
12
+ const { id, label, path, withData } = ctx
13
+ return withData({
14
+ id,
15
+ component: 'SkoponResumeSelect',
16
+ label,
17
+ placeholder: block.placeholder ?? '',
18
+ help: block.help ?? '',
19
+ enableRefresh: block.resumeEnableRefresh !== false,
20
+ resumeFilter: {
21
+ names: block.resumeFilter?.names ?? [],
22
+ agentUniqueIds: block.resumeFilter?.agentUniqueIds ?? [],
23
+ resumeUniqueIds: block.resumeFilter?.resumeUniqueIds ?? [],
24
+ pageSize: block.resumeFilter?.pageSize ?? 20,
25
+ },
26
+ value: { path },
27
+ })
28
+ }
29
+
30
+ export function resumeMultiselectFromComponent(
31
+ node: A2uiComponentNode,
32
+ base: (type: 'resume_multiselect') => FormBlock,
33
+ helpers: { asLiteral: (value: unknown) => string | undefined; nameFromValue: (value: unknown) => string },
34
+ ): FormBlock {
35
+ const name = helpers.nameFromValue(node.value)
36
+ const block: FormBlock = {
37
+ ...base('resume_multiselect'),
38
+ name,
39
+ resumeEnableRefresh: node.enableRefresh !== false,
40
+ }
41
+ const placeholder = helpers.asLiteral(node.placeholder)
42
+ if (placeholder) block.placeholder = placeholder
43
+ const help = helpers.asLiteral(node.help)
44
+ if (help) block.help = help
45
+ if (node.resumeFilter && typeof node.resumeFilter === 'object') {
46
+ const rf = node.resumeFilter as Record<string, unknown>
47
+ block.resumeFilter = {
48
+ names: Array.isArray(rf.names) ? rf.names.map(String) : [],
49
+ agentUniqueIds: Array.isArray(rf.agentUniqueIds) ? rf.agentUniqueIds.map(String) : [],
50
+ resumeUniqueIds: Array.isArray(rf.resumeUniqueIds) ? rf.resumeUniqueIds.map(String) : [],
51
+ pageSize: typeof rf.pageSize === 'number' ? rf.pageSize : 20,
52
+ }
53
+ }
54
+ return block
55
+ }
56
+
57
+ export { asLiteral }
@@ -0,0 +1 @@
1
+ export { SkoponResumeSelectImpl } from '../../catalog/skoponResumeSelect'
@@ -0,0 +1,14 @@
1
+ import type { BlockAdapterPlugin } from '../types'
2
+ import {
3
+ resumeMultiselectFromComponent,
4
+ resumeMultiselectToComponent,
5
+ } from './adapter'
6
+ import { SkoponResumeSelectImpl } from './catalog'
7
+
8
+ export const resumeMultiselectAdapter: BlockAdapterPlugin = {
9
+ type: 'resume_multiselect',
10
+ componentName: 'SkoponResumeSelect',
11
+ toComponent: resumeMultiselectToComponent,
12
+ fromComponent: resumeMultiselectFromComponent,
13
+ catalogComponents: [SkoponResumeSelectImpl],
14
+ }