@changfenhuang/dsh-genui 0.9.8 → 0.9.9
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/CHANGELOG.md +13 -0
- package/README.md +13 -8
- package/README.zh-CN.md +13 -8
- package/SKILL.md +5 -5
- package/lib/client.js +3 -3
- package/lib/index.js +1273 -338
- package/lib/types/client/blocks/advanced.d.ts +1 -1
- package/lib/types/client/blocks/checkbox.d.ts +17 -0
- package/lib/types/client/blocks/forms.d.ts +3 -8
- package/lib/types/client/blocks/state.d.ts +18 -14
- package/lib/types/client/dom-fence.d.ts +1 -1
- package/lib/types/client/genui-runtime/diagnostics.d.ts +18 -0
- package/lib/types/client/genui-runtime/index.d.ts +1 -0
- package/lib/types/client/genui-runtime/limits.d.ts +54 -0
- package/lib/types/client/genui-runtime/normalize.d.ts +15 -0
- package/lib/types/client/genui-runtime/schema.d.ts +73 -0
- package/lib/types/client/genui-runtime/value-utils.d.ts +18 -0
- package/lib/types/client/guard.d.ts +60 -71
- package/lib/types/client/interaction-store.d.ts +7 -2
- package/lib/types/client/panel-command.d.ts +1 -1
- package/lib/types/client/primitive-labels.d.ts +11 -0
- package/lib/types/client/spec.d.ts +17 -17
- package/lib/types/plugin/chart-contract.d.ts +6 -4
- package/lib/types/plugin/index.d.ts +1 -4
- package/package.json +74 -13
- package/src/client/GenuiBlock.tsx +27 -11
- package/src/client/blocks/advanced.tsx +6 -5
- package/src/client/blocks/charts.tsx +1 -1
- package/src/client/blocks/checkbox.tsx +60 -0
- package/src/client/blocks/diagram/index.tsx +1 -1
- package/src/client/blocks/forms.tsx +51 -28
- package/src/client/blocks/render-node.tsx +3 -16
- package/src/client/blocks/state.ts +18 -14
- package/src/client/dom-fence.tsx +2 -1
- package/src/client/fence-render.tsx +26 -15
- package/src/client/genui-runtime/diagnostics.ts +143 -0
- package/src/client/genui-runtime/index.ts +1 -0
- package/src/client/genui-runtime/limits.ts +54 -0
- package/src/client/genui-runtime/normalize.ts +115 -0
- package/src/client/genui-runtime/schema.ts +291 -0
- package/src/client/genui-runtime/value-utils.ts +67 -0
- package/src/client/guard.ts +314 -169
- package/src/client/index.tsx +3 -2
- package/src/client/interaction-store.ts +7 -2
- package/src/client/panel-command.ts +2 -1
- package/src/client/panel.tsx +1 -1
- package/src/client/primitive-labels.ts +37 -0
- package/src/client/spec.ts +17 -17
- package/src/client/toolview.tsx +2 -1
- package/src/index.ts +3 -6
- package/src/plugin/chart-contract.ts +7 -97
- package/src/plugin/index.ts +2 -6
- package/src/plugin/tool.ts +60 -40
|
@@ -101,4 +101,4 @@ export declare const BreadcrumbNode: import("react").NamedExoticComponent<{
|
|
|
101
101
|
* names stay independent. The model round-trip takes seconds, so a few
|
|
102
102
|
* hundred ms of trailing delay is imperceptible — and it stops bursts of
|
|
103
103
|
* queued user turns.
|
|
104
|
-
*/
|
|
104
|
+
*/
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { GenuiCheckbox } from '../spec.ts';
|
|
2
|
+
import type { AnswersState, GenuiBlockProps } from './state.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Checkbox with two backwards-compatible modes:
|
|
5
|
+
*
|
|
6
|
+
* - no `group`: legacy local toggle + optional per-click action;
|
|
7
|
+
* - with `group`: local-only multi-select state collected by a sibling submit.
|
|
8
|
+
*
|
|
9
|
+
* A persisted group key wins over `checked`, including an explicit empty
|
|
10
|
+
* array. This lets a user clear every default-checked option without those
|
|
11
|
+
* defaults reappearing after refresh.
|
|
12
|
+
*/
|
|
13
|
+
export declare function CheckboxNode({ node, onAction, answers }: {
|
|
14
|
+
node: GenuiCheckbox;
|
|
15
|
+
onAction?: GenuiBlockProps['onAction'];
|
|
16
|
+
answers?: AnswersState | undefined;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
@@ -7,14 +7,9 @@ export declare function RadioNode({ node, onAction, answers }: {
|
|
|
7
7
|
}): import("react").JSX.Element;
|
|
8
8
|
/** Resolve a question's correct label from its metadata. */
|
|
9
9
|
export declare function correctLabelOf(m: QuestionMeta): string | undefined;
|
|
10
|
-
/** Submit:
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* trip, and locks the questions until 重新作答 resets them. Only when NO
|
|
14
|
-
* question has answers does it fall back to firing ONE action
|
|
15
|
-
* (`{type:'submit', answers, total, answered}`). Disabled until the
|
|
16
|
-
* selection criteria are met (all listed groups answered, or ≥1 answer
|
|
17
|
-
* without a group list); the hint shows the progress. */
|
|
10
|
+
/** Submit: collect grouped radio and checkbox answers. Radio-only scopes keep
|
|
11
|
+
* LOCAL-FIRST grading; when checkbox selections participate, the click falls
|
|
12
|
+
* back to the aggregation action so multi-select data is never discarded. */
|
|
18
13
|
export declare function SubmitNode({ node, onAction, answers }: {
|
|
19
14
|
node: GenuiSubmit;
|
|
20
15
|
onAction?: GenuiBlockProps['onAction'];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared block-level state types: the block props contract and the answers
|
|
3
|
-
* registry (grouped radios → submit
|
|
4
|
-
* the per-family block modules can import the types without
|
|
5
|
-
* into the block shell.
|
|
3
|
+
* registry (grouped radios / checkboxes → submit aggregation). Lives OUTSIDE
|
|
4
|
+
* GenuiBlock.tsx so the per-family block modules can import the types without
|
|
5
|
+
* a cycle back into the block shell.
|
|
6
6
|
* @module @changfenhuang/dsh-genui/client/blocks/state
|
|
7
7
|
*/
|
|
8
8
|
import type { GenuiSpec } from '../spec.ts';
|
|
@@ -17,10 +17,11 @@ export interface GenuiBlockProps {
|
|
|
17
17
|
onAction?: ((action: string, payload: Record<string, unknown>) => void) | undefined;
|
|
18
18
|
/**
|
|
19
19
|
* v2.7: durable-state key (session + slot + content fingerprint). When set,
|
|
20
|
-
* interaction state (radio answers, submit lock, field
|
|
21
|
-
* localStorage and restores on refresh / re-render of
|
|
22
|
-
* Changing this key also starts a fresh in-memory
|
|
23
|
-
* callers only provide stateKey — GenuiBlock
|
|
20
|
+
* interaction state (radio answers, checkbox groups, submit lock, field
|
|
21
|
+
* values) persists to localStorage and restores on refresh / re-render of
|
|
22
|
+
* the same content. Changing this key also starts a fresh in-memory
|
|
23
|
+
* interaction-state lifetime; callers only provide stateKey — GenuiBlock
|
|
24
|
+
* owns the matching React identity.
|
|
24
25
|
*/
|
|
25
26
|
stateKey?: string | undefined;
|
|
26
27
|
}
|
|
@@ -33,15 +34,17 @@ export interface QuestionMeta {
|
|
|
33
34
|
/** Shown after local grading. */
|
|
34
35
|
explanation?: string | undefined;
|
|
35
36
|
}
|
|
36
|
-
/** Block-wide
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* plain group → chosen-option-label strings (the display label lives in
|
|
42
|
-
* QuestionMeta; localStorage stores the same string table — no migration). */
|
|
37
|
+
/** Block-wide aggregation state. Grouped radios store one selected label in
|
|
38
|
+
* `answers`; grouped checkboxes store a selected-label array in
|
|
39
|
+
* `multiAnswers`. Keeping the registries separate preserves the existing
|
|
40
|
+
* radio/local-grading string contract while allowing submit to expose one
|
|
41
|
+
* unified `answers` payload (`string | string[]`). */
|
|
43
42
|
export interface AnswersState {
|
|
44
43
|
answers: Record<string, string>;
|
|
44
|
+
/** Grouped checkbox selections. An empty array is meaningful: the user
|
|
45
|
+
* explicitly cleared the group, so model-provided `checked` defaults must
|
|
46
|
+
* not reappear after a durable restore. */
|
|
47
|
+
multiAnswers: Record<string, string[]>;
|
|
45
48
|
/** Field values by id (input/textarea with an `id`), collected by submit. */
|
|
46
49
|
fields: Record<string, string>;
|
|
47
50
|
/** Field ids whose value must never be persisted or collected (secrets). */
|
|
@@ -52,6 +55,7 @@ export interface AnswersState {
|
|
|
52
55
|
/** Bumped by every reset; radios use it as their remount key. */
|
|
53
56
|
round: number;
|
|
54
57
|
setAnswer: (group: string, choice: string) => void;
|
|
58
|
+
setMultiAnswer: (group: string, choice: string, checked: boolean) => void;
|
|
55
59
|
setField: (id: string, value: string) => void;
|
|
56
60
|
registerSecretField: (id: string) => void;
|
|
57
61
|
registerMeta: (group: string, meta: QuestionMeta) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Root } from 'react-dom/client';
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
import type { SessionId } from '@deepseek-ai/dsh-
|
|
3
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
4
4
|
/** Override the React root factory (tests / tuning). */
|
|
5
5
|
export declare function setDomRootFactory(factory: (container: HTMLElement) => Root): void;
|
|
6
6
|
/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface GenuiDiagnostic {
|
|
2
|
+
readonly kind: 'alias' | 'unknown-field';
|
|
3
|
+
readonly path: string;
|
|
4
|
+
readonly message: string;
|
|
5
|
+
readonly type?: string;
|
|
6
|
+
readonly field?: string;
|
|
7
|
+
readonly canonical?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Diagnose unknown direct fields on native nodes.
|
|
11
|
+
*
|
|
12
|
+
* Unknown types are intentionally skipped so custom renderers retain their
|
|
13
|
+
* opaque extension payloads. Native unknown fields are warnings, not errors.
|
|
14
|
+
*
|
|
15
|
+
* @param value - Canonical or raw GenUI value.
|
|
16
|
+
* @returns Stable field diagnostics in tree order.
|
|
17
|
+
*/
|
|
18
|
+
export declare function diagnoseUnknownGenuiFields(value: unknown): GenuiDiagnostic[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GENUI_LIMITS } from './limits.ts';
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Resource limits shared by GenUI repair, validation, and rendering. */
|
|
2
|
+
export declare const GENUI_LIMITS: {
|
|
3
|
+
/** Maximum nesting depth of the component tree. */
|
|
4
|
+
readonly maxDepth: 8;
|
|
5
|
+
/** Maximum total nodes across the whole spec. */
|
|
6
|
+
readonly maxNodes: 200;
|
|
7
|
+
/** Maximum length of any plain string field. */
|
|
8
|
+
readonly maxString: 2000;
|
|
9
|
+
/** Maximum length of a `code` body. */
|
|
10
|
+
readonly maxCode: 12000;
|
|
11
|
+
/** Maximum length of a mermaid source. */
|
|
12
|
+
readonly maxMermaid: 8000;
|
|
13
|
+
/** Maximum `grid` columns. */
|
|
14
|
+
readonly maxGridCols: 12;
|
|
15
|
+
/** Maximum `tabs` count. */
|
|
16
|
+
readonly maxTabs: 12;
|
|
17
|
+
/** Maximum `accordion` items. */
|
|
18
|
+
readonly maxAccordionItems: 24;
|
|
19
|
+
/** Maximum `list` items. */
|
|
20
|
+
readonly maxListItems: 50;
|
|
21
|
+
/** Maximum `select`/`radio` options. */
|
|
22
|
+
readonly maxOptions: 50;
|
|
23
|
+
/** Maximum `table` rows / columns. */
|
|
24
|
+
readonly maxTableRows: 50;
|
|
25
|
+
readonly maxTableCols: 12;
|
|
26
|
+
/** Maximum `chart` data points per series. */
|
|
27
|
+
readonly maxChartPoints: 60;
|
|
28
|
+
/** Maximum `plot` series and per-series parameters. */
|
|
29
|
+
readonly maxPlotSeries: 8;
|
|
30
|
+
readonly maxPlotParams: 6;
|
|
31
|
+
/** Maximum `scene3d` meshes. */
|
|
32
|
+
readonly maxMeshes: 5;
|
|
33
|
+
/** Maximum `quiz` options. */
|
|
34
|
+
readonly maxQuizOptions: 8;
|
|
35
|
+
/** Maximum `steps` / `timeline` / `breadcrumb` / `keyvalue` entries. */
|
|
36
|
+
readonly maxSteps: 24;
|
|
37
|
+
readonly maxTimelineItems: 24;
|
|
38
|
+
readonly maxBreadcrumbItems: 12;
|
|
39
|
+
readonly maxKeyValuePairs: 24;
|
|
40
|
+
/** Maximum `file-tree` nesting. */
|
|
41
|
+
readonly maxTreeDepth: 6;
|
|
42
|
+
/** Maximum `diagram` nodes / edges / zones / focal accents. */
|
|
43
|
+
readonly maxDiagramNodes: 9;
|
|
44
|
+
readonly maxDiagramEdges: 12;
|
|
45
|
+
readonly maxDiagramZones: 3;
|
|
46
|
+
readonly maxDiagramFocal: 2;
|
|
47
|
+
readonly maxDiagramLabel: 14;
|
|
48
|
+
/** Maximum depth of an `echart` option object. */
|
|
49
|
+
readonly maxEChartOptionDepth: 10;
|
|
50
|
+
/** Maximum length of any single array inside an `echart` option. */
|
|
51
|
+
readonly maxEChartArrayLen: 500;
|
|
52
|
+
/** Maximum entries traversed while sanitizing an `echart` option. */
|
|
53
|
+
readonly maxEChartOptionNodes: 2000;
|
|
54
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { GenuiDiagnostic } from './diagnostics.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a raw GenUI value into canonical field names.
|
|
4
|
+
*
|
|
5
|
+
* Only deterministic aliases and structural aliases are changed. Resource
|
|
6
|
+
* limits, type repair, security filtering, and semantic validation remain in
|
|
7
|
+
* the guard layer. Unknown component types are returned opaque.
|
|
8
|
+
*
|
|
9
|
+
* @param value - Raw GenUI spec or bare native component.
|
|
10
|
+
* @returns Canonical value and stable alias diagnostics.
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizeGenuiSpec(value: unknown): {
|
|
13
|
+
value: unknown;
|
|
14
|
+
warnings: GenuiDiagnostic[];
|
|
15
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Runtime metadata shared by GenUI normalization and diagnostics. */
|
|
2
|
+
export type ComponentFieldKind = 'string' | 'string-or-null' | 'number' | 'boolean' | 'nodes' | 'array' | 'object' | 'unknown';
|
|
3
|
+
/** A conditional rule for fields whose presence depends on another field. */
|
|
4
|
+
export interface ComponentConditionalRule {
|
|
5
|
+
readonly kind: 'required-if';
|
|
6
|
+
readonly when: {
|
|
7
|
+
readonly field: string;
|
|
8
|
+
readonly equals: unknown;
|
|
9
|
+
};
|
|
10
|
+
readonly required: readonly string[];
|
|
11
|
+
readonly message?: string;
|
|
12
|
+
}
|
|
13
|
+
/** A field rule that requires at least one member of a field group. */
|
|
14
|
+
export interface ComponentOneOfRule {
|
|
15
|
+
readonly kind: 'one-of-required';
|
|
16
|
+
readonly fields: readonly string[];
|
|
17
|
+
readonly message?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Runtime metadata naming a component-specific semantic validator. */
|
|
20
|
+
export interface ComponentValidatorMetadata {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly [key: string]: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** Runtime schema for an object nested inside a native component field. */
|
|
25
|
+
export interface ComponentRecordSchema {
|
|
26
|
+
readonly required: readonly string[];
|
|
27
|
+
readonly fields: Readonly<Record<string, ComponentFieldKind>>;
|
|
28
|
+
readonly enums: Readonly<Record<string, readonly string[]>>;
|
|
29
|
+
readonly nested: Readonly<Record<string, ComponentRecordSchema>>;
|
|
30
|
+
}
|
|
31
|
+
export interface ComponentSchema {
|
|
32
|
+
readonly required: readonly string[];
|
|
33
|
+
readonly fields: Readonly<Record<string, ComponentFieldKind>>;
|
|
34
|
+
readonly enums: Readonly<Record<string, readonly string[]>>;
|
|
35
|
+
/** Explicit optional field kinds; `fields` remains the complete field map. */
|
|
36
|
+
readonly optional: Readonly<Record<string, ComponentFieldKind>>;
|
|
37
|
+
readonly aliases: Readonly<Record<string, string>>;
|
|
38
|
+
readonly oneOfRequired: readonly (readonly string[])[];
|
|
39
|
+
readonly conditionalRequired: readonly ComponentConditionalRule[];
|
|
40
|
+
readonly rules: readonly (ComponentOneOfRule | ComponentConditionalRule)[];
|
|
41
|
+
readonly nested: Readonly<Record<string, ComponentRecordSchema>>;
|
|
42
|
+
readonly validator?: ComponentValidatorMetadata;
|
|
43
|
+
}
|
|
44
|
+
/** Canonical enum domains shared by schema validation and repair. */
|
|
45
|
+
export declare const TEXT_SIZES: readonly ["h1", "h2", "h3", "body", "muted", "caption"];
|
|
46
|
+
export declare const BUTTON_TONES: readonly ["primary", "danger", "success", "ghost"];
|
|
47
|
+
export declare const BADGE_TONES: readonly ["success", "warn", "danger", "accent"];
|
|
48
|
+
export declare const INPUT_TYPES: readonly ["text", "email", "password", "color"];
|
|
49
|
+
export declare const CALLOUT_TONES: readonly ["info", "success", "warning", "error"];
|
|
50
|
+
export declare const CHART_KINDS: readonly ["bars", "line", "donut"];
|
|
51
|
+
export declare const PLOT_KINDS: readonly ["line", "area", "scatter"];
|
|
52
|
+
export declare const MEDIA_ASPECT_RATIOS: readonly ["16:9", "4:3", "1:1", "9:16"];
|
|
53
|
+
export declare const MESH_SHAPES: readonly ["box", "sphere", "cone", "cylinder", "torus"];
|
|
54
|
+
export declare const FILE_TYPES: readonly ["file", "dir"];
|
|
55
|
+
export declare const DIAGRAM_KINDS: readonly ["architecture", "it-state", "flowchart", "sequence", "state", "er", "timeline", "swimlane", "quadrant", "radar", "loop", "nested", "tree", "org-chart", "layers", "venn", "pyramid", "bar", "line", "gantt", "scatter", "high-level", "process", "medallion", "data-flow", "dp-integration", "dp-security-matrix"];
|
|
56
|
+
export declare const DIAGRAM_NODE_TYPES: readonly ["focal", "backend", "store", "external", "input", "optional", "security"];
|
|
57
|
+
export declare const DIAGRAM_VARIANTS: readonly ["light", "dark", "editorial"];
|
|
58
|
+
export declare const DIAGRAM_EDGE_KINDS: readonly ["solid", "dashed", "accent", "link"];
|
|
59
|
+
export declare const DIAGRAM_ROUTES: readonly ["auto", "orthogonal", "straight"];
|
|
60
|
+
export declare const ECHART_PRESETS: readonly ["bar", "line", "area", "pie", "scatter"];
|
|
61
|
+
/** Root GenUI specification metadata used by diagnostics. */
|
|
62
|
+
export declare const GENUI_SPEC_SCHEMA: ComponentSchema;
|
|
63
|
+
/** Backwards-friendly short alias for the root specification schema. */
|
|
64
|
+
export declare const SPEC_SCHEMA: ComponentSchema;
|
|
65
|
+
/**
|
|
66
|
+
* Native component field metadata.
|
|
67
|
+
*
|
|
68
|
+
* This is intentionally explicit rather than inferred from TypeScript
|
|
69
|
+
* interfaces: the registry is also consumed at runtime by normalization and
|
|
70
|
+
* diagnostics, where erased interfaces are unavailable.
|
|
71
|
+
*/
|
|
72
|
+
export declare const COMPONENT_SCHEMAS: Readonly<Record<string, ComponentSchema>>;
|
|
73
|
+
export declare const GENUI_NATIVE_TYPES: ReadonlySet<string>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Shared primitive sanitizers used by the GenUI guard. */
|
|
2
|
+
/** String field: truncate a string to `cap`, or undefined when not a string. */
|
|
3
|
+
export declare function str(value: unknown, cap: number): string | undefined;
|
|
4
|
+
export declare function color(value: unknown): string | undefined;
|
|
5
|
+
/** Keep only http(s) and mailto link targets. */
|
|
6
|
+
export declare function safeHref(value: unknown): string | undefined;
|
|
7
|
+
/** Keep browser-reachable http(s) or same-origin relative media paths. */
|
|
8
|
+
export declare function safeMediaSrc(value: unknown): string | undefined;
|
|
9
|
+
/** Finite-number field: clamp into [min, max], or undefined when not finite. */
|
|
10
|
+
export declare function num(value: unknown, min: number, max: number): number | undefined;
|
|
11
|
+
/** Integer field: clamp into [min, max], or undefined when not a finite number. */
|
|
12
|
+
export declare function int(value: unknown, min: number, max: number): number | undefined;
|
|
13
|
+
/** Optional enum field: the value when it matches, otherwise undefined. */
|
|
14
|
+
export declare function enu<T extends string>(value: unknown, values: readonly T[]): T | undefined;
|
|
15
|
+
/** Plain object (not array, not null). */
|
|
16
|
+
export declare function obj(value: unknown): Record<string, unknown> | undefined;
|
|
17
|
+
/** Preserve an optional field only when its value exists. */
|
|
18
|
+
export declare function opt<K extends string, V>(key: K, value: V | undefined): Partial<Record<K, V>>;
|
|
@@ -19,66 +19,7 @@
|
|
|
19
19
|
* are elided.
|
|
20
20
|
*/
|
|
21
21
|
import type { GenuiSpec } from './spec.ts';
|
|
22
|
-
|
|
23
|
-
export declare const GENUI_LIMITS: {
|
|
24
|
-
/** Maximum nesting depth of the component tree. */
|
|
25
|
-
readonly maxDepth: 8;
|
|
26
|
-
/** Maximum total nodes across the whole spec. */
|
|
27
|
-
readonly maxNodes: 200;
|
|
28
|
-
/** Maximum length of any plain string field. */
|
|
29
|
-
readonly maxString: 2000;
|
|
30
|
-
/** Maximum length of a `code` body. */
|
|
31
|
-
readonly maxCode: 12000;
|
|
32
|
-
/** Maximum length of a mermaid source. */
|
|
33
|
-
readonly maxMermaid: 8000;
|
|
34
|
-
/** Maximum `grid` columns. */
|
|
35
|
-
readonly maxGridCols: 12;
|
|
36
|
-
/** Maximum `tabs` count. */
|
|
37
|
-
readonly maxTabs: 12;
|
|
38
|
-
/** Maximum `accordion` items. */
|
|
39
|
-
readonly maxAccordionItems: 24;
|
|
40
|
-
/** Maximum `list` items. */
|
|
41
|
-
readonly maxListItems: 50;
|
|
42
|
-
/** Maximum `select`/`radio` options. */
|
|
43
|
-
readonly maxOptions: 50;
|
|
44
|
-
/** Maximum `table` rows / columns. */
|
|
45
|
-
readonly maxTableRows: 50;
|
|
46
|
-
readonly maxTableCols: 12;
|
|
47
|
-
/** Maximum `chart` data points per series. */
|
|
48
|
-
readonly maxChartPoints: 60;
|
|
49
|
-
/** Maximum `plot` series and per-series parameters. */
|
|
50
|
-
readonly maxPlotSeries: 8;
|
|
51
|
-
readonly maxPlotParams: 6;
|
|
52
|
-
/** Maximum `scene3d` meshes. */
|
|
53
|
-
readonly maxMeshes: 5;
|
|
54
|
-
/** Maximum `quiz` options. */
|
|
55
|
-
readonly maxQuizOptions: 8;
|
|
56
|
-
/** Maximum `steps` / `timeline` / `breadcrumb` / `keyvalue` entries. */
|
|
57
|
-
readonly maxSteps: 24;
|
|
58
|
-
readonly maxTimelineItems: 24;
|
|
59
|
-
readonly maxBreadcrumbItems: 12;
|
|
60
|
-
readonly maxKeyValuePairs: 24;
|
|
61
|
-
/** Maximum `file-tree` nesting. */
|
|
62
|
-
readonly maxTreeDepth: 6;
|
|
63
|
-
/** Maximum `diagram` nodes / edges / zones / focal accents (editorial
|
|
64
|
-
* complexity budget, mirroring diagram-design's §7 limits). */
|
|
65
|
-
readonly maxDiagramNodes: 9;
|
|
66
|
-
readonly maxDiagramEdges: 12;
|
|
67
|
-
readonly maxDiagramZones: 3;
|
|
68
|
-
readonly maxDiagramFocal: 2;
|
|
69
|
-
readonly maxDiagramLabel: 14;
|
|
70
|
-
/** Maximum depth of an `echart` option object (prevents pathological nested
|
|
71
|
-
* ECharts configs from stalling the guard walk). */
|
|
72
|
-
readonly maxEChartOptionDepth: 10;
|
|
73
|
-
/** Maximum length of any single array inside an `echart` option (prevents
|
|
74
|
-
* a model from stalling rendering with `series.data` of hundreds of
|
|
75
|
-
* thousands of points). */
|
|
76
|
-
readonly maxEChartArrayLen: 500;
|
|
77
|
-
/** Maximum total entries (object keys + array elements) traversed while
|
|
78
|
-
* sanitizing an `echart` option. Bounds the walk so a pathologically
|
|
79
|
-
* large option object cannot stall the guard. */
|
|
80
|
-
readonly maxEChartOptionNodes: 2000;
|
|
81
|
-
};
|
|
22
|
+
import type { GenuiDiagnostic } from './genui-runtime/diagnostics.ts';
|
|
82
23
|
/** Result of `validateGenuiSpec`. */
|
|
83
24
|
export interface GenuiValidation {
|
|
84
25
|
ok: boolean;
|
|
@@ -86,12 +27,14 @@ export interface GenuiValidation {
|
|
|
86
27
|
errors: string[];
|
|
87
28
|
}
|
|
88
29
|
/**
|
|
89
|
-
* Deterministically repair a raw spec
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
30
|
+
* Deterministically repair a raw spec into a renderable GenuiSpec.
|
|
31
|
+
*
|
|
32
|
+
* Alias normalization is performed before the existing resource, type, and
|
|
33
|
+
* security repair rules. The result remains idempotent and keeps the legacy
|
|
34
|
+
* public API used by the fence renderer and client components.
|
|
35
|
+
*
|
|
36
|
+
* @param value - Raw GenUI spec or bare component.
|
|
37
|
+
* @returns Repaired canonical spec, or null for an invalid root.
|
|
95
38
|
*/
|
|
96
39
|
export declare function repairGenuiSpec(value: unknown): GenuiSpec | null;
|
|
97
40
|
/**
|
|
@@ -115,6 +58,8 @@ export declare const GENUI_NODE_TYPES: ReadonlySet<string>;
|
|
|
115
58
|
* @returns declared node count up to the ceiling.
|
|
116
59
|
*/
|
|
117
60
|
export declare function countDeclaredGenuiNodes(value: unknown, cap?: number): number;
|
|
61
|
+
/** Count native nodes that survived repair, excluding opaque custom nodes. */
|
|
62
|
+
export declare function countRenderedNativeGenuiNodes(value: unknown, cap?: number): number;
|
|
118
63
|
/**
|
|
119
64
|
* Return field-level chart errors without changing other repairable component families.
|
|
120
65
|
* @param value - raw GenUI spec.
|
|
@@ -122,10 +67,54 @@ export declare function countDeclaredGenuiNodes(value: unknown, cap?: number): n
|
|
|
122
67
|
*/
|
|
123
68
|
export declare function validateGenuiChartSemantics(value: unknown): string[];
|
|
124
69
|
/**
|
|
125
|
-
* Validate a raw
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
70
|
+
* Validate a raw GenUI value after deterministic alias normalization.
|
|
71
|
+
*
|
|
72
|
+
* Validation therefore only sees canonical fields; repairable aliases do not
|
|
73
|
+
* produce false required-field errors. Structural and semantic defects remain
|
|
74
|
+
* errors, while native unknown fields are exposed by the warning diagnostics
|
|
75
|
+
* returned from `processGenuiSpec`.
|
|
76
|
+
*
|
|
77
|
+
* @param value - Raw GenUI spec or bare component.
|
|
78
|
+
* @returns Validation result with human-readable errors.
|
|
130
79
|
*/
|
|
131
80
|
export declare function validateGenuiSpec(value: unknown): GenuiValidation;
|
|
81
|
+
export interface GenuiProcessResult {
|
|
82
|
+
/** Canonical alias-normalized input value. */
|
|
83
|
+
value: unknown;
|
|
84
|
+
/** Alias-normalized value, provided as an explicit descriptive alias. */
|
|
85
|
+
normalized: unknown;
|
|
86
|
+
/** Canonical repaired value consumed by the renderer. */
|
|
87
|
+
repaired: GenuiSpec | null;
|
|
88
|
+
/** Renderer-facing alias for `repaired`. */
|
|
89
|
+
spec: GenuiSpec | null;
|
|
90
|
+
/** Structural and semantic validation errors. */
|
|
91
|
+
errors: string[];
|
|
92
|
+
/** Alias and native unknown-field warnings. */
|
|
93
|
+
warnings: GenuiDiagnostic[];
|
|
94
|
+
/** Native nodes declared in the canonical input, before repair drops. */
|
|
95
|
+
declaredCount: number;
|
|
96
|
+
/** Nodes present in the repaired tree. */
|
|
97
|
+
renderedCount: number;
|
|
98
|
+
/** Native nodes declared before repair, excluding custom payloads. */
|
|
99
|
+
declaredNativeCount: number;
|
|
100
|
+
/** Native nodes present after repair, excluding custom and file-tree data. */
|
|
101
|
+
renderedNativeCount: number;
|
|
102
|
+
/** All rendered component nodes, including opaque custom nodes. */
|
|
103
|
+
renderedTotalCount: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Run the shared canonical GenUI pipeline.
|
|
107
|
+
*
|
|
108
|
+
* The pipeline normalizes aliases, applies the existing deterministic repair
|
|
109
|
+
* and security filters, validates the canonical input, and reports native
|
|
110
|
+
* declarations that disappeared during repair. Custom nodes stay opaque and
|
|
111
|
+
* do not participate in native unknown-field diagnostics.
|
|
112
|
+
*
|
|
113
|
+
* @param value - Raw GenUI spec or bare component.
|
|
114
|
+
* @returns Canonical input, repaired output, diagnostics, and node counts.
|
|
115
|
+
*/
|
|
116
|
+
export declare function processGenuiSpec(value: unknown): GenuiProcessResult;
|
|
117
|
+
/** Return whether the only process errors describe an intentional budget tail cut. */
|
|
118
|
+
export declare function isIntentionalBudgetCut(processed: GenuiProcessResult): boolean;
|
|
119
|
+
/** Decide whether a repaired spec is safe to expose to any GenUI renderer. */
|
|
120
|
+
export declare function isRenderableProcess(processed: GenuiProcessResult): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Interaction-state store: durable per-block GenUI interaction state
|
|
3
|
-
* (radio answers, submit lock, input/textarea values) in
|
|
3
|
+
* (radio answers, checkbox groups, submit lock, input/textarea values) in
|
|
4
|
+
* localStorage.
|
|
4
5
|
*
|
|
5
6
|
* LOCAL-FIRST persistence: a ```dsh-ui block's interactive state survives
|
|
6
7
|
* page refresh and session reopen because it is keyed by
|
|
@@ -10,13 +11,17 @@
|
|
|
10
11
|
* share state because their content fingerprints differ.
|
|
11
12
|
*
|
|
12
13
|
* Bounded: at most MAX_BLOCKS entries, LRU-evicted on write; each block's
|
|
13
|
-
* payload is small (
|
|
14
|
+
* payload is small (answer maps + a few field values).
|
|
14
15
|
* @module @changfenhuang/dsh-genui/client/interaction-store
|
|
15
16
|
*/
|
|
16
17
|
/** Durable state of one UI block. */
|
|
17
18
|
export interface BlockInteractionState {
|
|
18
19
|
/** group → chosen option label (radio aggregation answers). */
|
|
19
20
|
answers?: Record<string, string>;
|
|
21
|
+
/** group → chosen option labels (checkbox aggregation answers). Empty arrays
|
|
22
|
+
* are retained so an explicitly cleared group wins over `checked` defaults
|
|
23
|
+
* after refresh. */
|
|
24
|
+
multiAnswers?: Record<string, string[]>;
|
|
20
25
|
/** True after a local grading: the paper stays graded across refresh. */
|
|
21
26
|
locked?: boolean;
|
|
22
27
|
/** field id → current value (input/textarea with an `id`). */
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* re-run render_ui) or through another /panel.
|
|
16
16
|
*/
|
|
17
17
|
import type { InputTriggerSource } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
|
|
18
|
-
import type { SessionId } from '@deepseek-ai/dsh-
|
|
18
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
19
19
|
import type { GenuiSpec } from './spec.ts';
|
|
20
20
|
/** Default panel content published by `/panel`: the component overview. */
|
|
21
21
|
export declare const DEFAULT_PANEL_SPEC: GenuiSpec;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Localized labels required by the rc.1 DSH UI primitives. */
|
|
2
|
+
import type { DiffBlockLabels, JsonTreeLabels } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
/** Chrome labels for an inline GenUI diff block. */
|
|
4
|
+
export declare const DIFF_BLOCK_LABELS: DiffBlockLabels;
|
|
5
|
+
/** Copy labels for a code block rendered inside a GenUI fence or node. */
|
|
6
|
+
export declare const CODE_BLOCK_LABELS: {
|
|
7
|
+
readonly copyLabel: "复制";
|
|
8
|
+
readonly copiedLabel: "复制成功";
|
|
9
|
+
};
|
|
10
|
+
/** Chrome labels for an inline GenUI JSON tree. */
|
|
11
|
+
export declare const JSON_TREE_LABELS: JsonTreeLabels;
|
|
@@ -51,7 +51,7 @@ export interface GenuiInput {
|
|
|
51
51
|
label?: string;
|
|
52
52
|
placeholder?: string;
|
|
53
53
|
value?: string;
|
|
54
|
-
inputType?: 'text' | 'email' | 'password';
|
|
54
|
+
inputType?: 'text' | 'email' | 'password' | 'color';
|
|
55
55
|
/** v2: when set, interaction sends this action back to the model. */
|
|
56
56
|
action?: string;
|
|
57
57
|
/**
|
|
@@ -84,6 +84,12 @@ export interface GenuiCheckbox {
|
|
|
84
84
|
checked?: boolean;
|
|
85
85
|
/** v2: when set, interaction sends this action back to the model. */
|
|
86
86
|
action?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Aggregation group name. When set, toggles stay local and update the
|
|
89
|
+
* block-wide multi-answer registry instead of firing the per-click action;
|
|
90
|
+
* a sibling `submit` collects the selected labels as a string array.
|
|
91
|
+
*/
|
|
92
|
+
group?: string;
|
|
87
93
|
}
|
|
88
94
|
export interface GenuiLink {
|
|
89
95
|
type: 'link';
|
|
@@ -314,23 +320,17 @@ export interface GenuiRadio {
|
|
|
314
320
|
/** v2.6: per-question explanation shown after local grading. */
|
|
315
321
|
explanation?: string;
|
|
316
322
|
}
|
|
317
|
-
/** Submit node: collects
|
|
318
|
-
* LOCAL-FIRST
|
|
319
|
-
*
|
|
320
|
-
*
|
|
321
|
-
* NO question carries answers does it fall back to firing ONE action
|
|
322
|
-
* (`{type:'submit', answers, total, answered}`). */
|
|
323
|
+
/** Submit node: collects sibling `radio` and grouped `checkbox` answers in
|
|
324
|
+
* this block. LOCAL-FIRST grading still applies to radio-only question scopes;
|
|
325
|
+
* aggregation scopes can emit strings for radio groups and string arrays for
|
|
326
|
+
* checkbox groups in the same `answers` object. */
|
|
323
327
|
export interface GenuiSubmit {
|
|
324
328
|
type: 'submit';
|
|
325
329
|
label: string;
|
|
326
330
|
/**
|
|
327
|
-
* Optional action name. Local-first: when
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
* question has local answers does the submit need an action to collect
|
|
331
|
-
* `{type:'submit', answers, total, answered}`; without one the button
|
|
332
|
-
* renders disabled (honest affordance). Also fired as the reset hook if
|
|
333
|
-
* `resetAction` is absent.
|
|
331
|
+
* Optional action name. Local-first: when the in-scope questions can be
|
|
332
|
+
* graded entirely locally, no action is needed. Aggregation submits require
|
|
333
|
+
* an action and emit `{type:'submit', answers, total, answered}`.
|
|
334
334
|
*/
|
|
335
335
|
action?: string;
|
|
336
336
|
/**
|
|
@@ -341,9 +341,9 @@ export interface GenuiSubmit {
|
|
|
341
341
|
resetAction?: string;
|
|
342
342
|
/**
|
|
343
343
|
* Optional explicit group list to wait for; when absent the submit enables
|
|
344
|
-
* once at least one grouped
|
|
345
|
-
* disabled until EVERY listed group has a
|
|
346
|
-
* the progress).
|
|
344
|
+
* once at least one grouped answer (or filled field) exists. When present
|
|
345
|
+
* it stays disabled until EVERY listed radio/checkbox group has a non-empty
|
|
346
|
+
* recorded answer (the hint shows the progress).
|
|
347
347
|
*/
|
|
348
348
|
groups?: string[];
|
|
349
349
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Validate
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Validate chart nodes using the guard's shared native renderability contract.
|
|
3
|
+
* The plugin-facing API remains stable while the guard remains the single
|
|
4
|
+
* owner of chart field and renderer-semantic rules.
|
|
5
|
+
*
|
|
6
|
+
* @param value - Raw GenUI spec or bare chart node.
|
|
7
|
+
* @returns Chart errors in deterministic tree order.
|
|
6
8
|
*/
|
|
7
9
|
export declare function validateRenderableChartSemantics(value: unknown): string[];
|
|
@@ -5,13 +5,10 @@
|
|
|
5
5
|
* model the language exists, so a session without the plugin simply never
|
|
6
6
|
* emits fences and nothing changes.
|
|
7
7
|
*
|
|
8
|
-
* The section
|
|
9
|
-
* guidance so the model sees it among its output-format rules.
|
|
8
|
+
* The section uses the host's centrally allocated structured-output placement.
|
|
10
9
|
* @module @changfenhuang/dsh-genui
|
|
11
10
|
*/
|
|
12
11
|
import { Context } from '@deepseek-ai/cordis';
|
|
13
|
-
/** Convention: tool guidance uses 100–199; bash's section is 104. */
|
|
14
|
-
export declare const GENUI_SECTION_ORDER = 105;
|
|
15
12
|
/** The fence language description injected into every assembled system prompt.
|
|
16
13
|
* Deliberately slim: the `genui` skill carries the full component→field
|
|
17
14
|
* mapping; this section keeps only the contract that must always be
|