@the-syllabus/analysis-renderers 0.2.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.
Files changed (128) hide show
  1. package/dist/cells/RelationshipCardCell.d.ts +10 -0
  2. package/dist/cells/RelationshipCardCell.d.ts.map +1 -0
  3. package/dist/cells/RelationshipCardCell.js +91 -0
  4. package/dist/cells/RelationshipCardCell.js.map +1 -0
  5. package/dist/cells/TacticCardCell.d.ts +12 -0
  6. package/dist/cells/TacticCardCell.d.ts.map +1 -0
  7. package/dist/cells/TacticCardCell.js +77 -0
  8. package/dist/cells/TacticCardCell.js.map +1 -0
  9. package/dist/cells/TemplateCardCell.d.ts +29 -0
  10. package/dist/cells/TemplateCardCell.d.ts.map +1 -0
  11. package/dist/cells/TemplateCardCell.js +202 -0
  12. package/dist/cells/TemplateCardCell.js.map +1 -0
  13. package/dist/cells/index.d.ts +15 -0
  14. package/dist/cells/index.d.ts.map +1 -0
  15. package/dist/cells/index.js +85 -0
  16. package/dist/cells/index.js.map +1 -0
  17. package/dist/components/ConditionCards.d.ts +18 -0
  18. package/dist/components/ConditionCards.d.ts.map +1 -0
  19. package/dist/components/ConditionCards.js +28 -0
  20. package/dist/components/ConditionCards.js.map +1 -0
  21. package/dist/components/EvidenceTrail.d.ts +54 -0
  22. package/dist/components/EvidenceTrail.d.ts.map +1 -0
  23. package/dist/components/EvidenceTrail.js +98 -0
  24. package/dist/components/EvidenceTrail.js.map +1 -0
  25. package/dist/dispatch/SubRendererDispatch.d.ts +39 -0
  26. package/dist/dispatch/SubRendererDispatch.d.ts.map +1 -0
  27. package/dist/dispatch/SubRendererDispatch.js +153 -0
  28. package/dist/dispatch/SubRendererDispatch.js.map +1 -0
  29. package/dist/hooks/useProseExtraction.d.ts +38 -0
  30. package/dist/hooks/useProseExtraction.d.ts.map +1 -0
  31. package/dist/hooks/useProseExtraction.js +93 -0
  32. package/dist/hooks/useProseExtraction.js.map +1 -0
  33. package/dist/index.d.ts +32 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +38 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/provenance/ProvenanceSectionIcon.d.ts +15 -0
  38. package/dist/provenance/ProvenanceSectionIcon.d.ts.map +1 -0
  39. package/dist/provenance/ProvenanceSectionIcon.js +11 -0
  40. package/dist/provenance/ProvenanceSectionIcon.js.map +1 -0
  41. package/dist/renderers/AccordionRenderer.d.ts +29 -0
  42. package/dist/renderers/AccordionRenderer.d.ts.map +1 -0
  43. package/dist/renderers/AccordionRenderer.js +315 -0
  44. package/dist/renderers/AccordionRenderer.js.map +1 -0
  45. package/dist/renderers/CardGridRenderer.d.ts +24 -0
  46. package/dist/renderers/CardGridRenderer.d.ts.map +1 -0
  47. package/dist/renderers/CardGridRenderer.js +321 -0
  48. package/dist/renderers/CardGridRenderer.js.map +1 -0
  49. package/dist/renderers/CardRenderer.d.ts +27 -0
  50. package/dist/renderers/CardRenderer.d.ts.map +1 -0
  51. package/dist/renderers/CardRenderer.js +337 -0
  52. package/dist/renderers/CardRenderer.js.map +1 -0
  53. package/dist/renderers/IdeaEvolutionRenderer.d.ts +16 -0
  54. package/dist/renderers/IdeaEvolutionRenderer.d.ts.map +1 -0
  55. package/dist/renderers/IdeaEvolutionRenderer.js +187 -0
  56. package/dist/renderers/IdeaEvolutionRenderer.js.map +1 -0
  57. package/dist/renderers/ProseRenderer.d.ts +10 -0
  58. package/dist/renderers/ProseRenderer.d.ts.map +1 -0
  59. package/dist/renderers/ProseRenderer.js +42 -0
  60. package/dist/renderers/ProseRenderer.js.map +1 -0
  61. package/dist/renderers/RawJsonRenderer.d.ts +8 -0
  62. package/dist/renderers/RawJsonRenderer.d.ts.map +1 -0
  63. package/dist/renderers/RawJsonRenderer.js +17 -0
  64. package/dist/renderers/RawJsonRenderer.js.map +1 -0
  65. package/dist/renderers/StatSummaryRenderer.d.ts +12 -0
  66. package/dist/renderers/StatSummaryRenderer.d.ts.map +1 -0
  67. package/dist/renderers/StatSummaryRenderer.js +93 -0
  68. package/dist/renderers/StatSummaryRenderer.js.map +1 -0
  69. package/dist/renderers/SynthesisRenderer.d.ts +15 -0
  70. package/dist/renderers/SynthesisRenderer.d.ts.map +1 -0
  71. package/dist/renderers/SynthesisRenderer.js +60 -0
  72. package/dist/renderers/SynthesisRenderer.js.map +1 -0
  73. package/dist/renderers/TableRenderer.d.ts +19 -0
  74. package/dist/renderers/TableRenderer.d.ts.map +1 -0
  75. package/dist/renderers/TableRenderer.js +273 -0
  76. package/dist/renderers/TableRenderer.js.map +1 -0
  77. package/dist/styles/accordion.css +376 -0
  78. package/dist/styles/index.css +5 -0
  79. package/dist/styles/renderers.css +1049 -0
  80. package/dist/sub-renderers/SubRenderers.d.ts +73 -0
  81. package/dist/sub-renderers/SubRenderers.d.ts.map +1 -0
  82. package/dist/sub-renderers/SubRenderers.js +2462 -0
  83. package/dist/sub-renderers/SubRenderers.js.map +1 -0
  84. package/dist/tokens/DesignTokenContext.d.ts +40 -0
  85. package/dist/tokens/DesignTokenContext.d.ts.map +1 -0
  86. package/dist/tokens/DesignTokenContext.js +408 -0
  87. package/dist/tokens/DesignTokenContext.js.map +1 -0
  88. package/dist/types/designTokens.d.ts +220 -0
  89. package/dist/types/designTokens.d.ts.map +1 -0
  90. package/dist/types/designTokens.js +8 -0
  91. package/dist/types/designTokens.js.map +1 -0
  92. package/dist/types/index.d.ts +32 -0
  93. package/dist/types/index.d.ts.map +1 -0
  94. package/dist/types/index.js +5 -0
  95. package/dist/types/index.js.map +1 -0
  96. package/dist/types/styles.d.ts +38 -0
  97. package/dist/types/styles.d.ts.map +1 -0
  98. package/dist/types/styles.js +14 -0
  99. package/dist/types/styles.js.map +1 -0
  100. package/dist/utils/tokenFlattener.d.ts +14 -0
  101. package/dist/utils/tokenFlattener.d.ts.map +1 -0
  102. package/dist/utils/tokenFlattener.js +56 -0
  103. package/dist/utils/tokenFlattener.js.map +1 -0
  104. package/package.json +31 -0
  105. package/src/cells/TemplateCardCell.tsx +439 -0
  106. package/src/cells/index.ts +98 -0
  107. package/src/components/ConditionCards.tsx +109 -0
  108. package/src/components/EvidenceTrail.tsx +203 -0
  109. package/src/dispatch/SubRendererDispatch.tsx +282 -0
  110. package/src/hooks/useProseExtraction.ts +125 -0
  111. package/src/index.ts +82 -0
  112. package/src/provenance/ProvenanceSectionIcon.tsx +19 -0
  113. package/src/renderers/AccordionRenderer.tsx +609 -0
  114. package/src/renderers/CardGridRenderer.tsx +608 -0
  115. package/src/renderers/CardRenderer.tsx +517 -0
  116. package/src/renderers/ProseRenderer.tsx +85 -0
  117. package/src/renderers/RawJsonRenderer.tsx +37 -0
  118. package/src/renderers/StatSummaryRenderer.tsx +182 -0
  119. package/src/renderers/TableRenderer.tsx +470 -0
  120. package/src/styles/accordion.css +376 -0
  121. package/src/styles/index.css +5 -0
  122. package/src/styles/renderers.css +1049 -0
  123. package/src/sub-renderers/SubRenderers.tsx +3487 -0
  124. package/src/tokens/DesignTokenContext.tsx +502 -0
  125. package/src/types/designTokens.ts +236 -0
  126. package/src/types/index.ts +53 -0
  127. package/src/types/styles.ts +44 -0
  128. package/src/utils/tokenFlattener.ts +64 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Design Token System - TypeScript Interfaces
3
+ *
4
+ * Mirrors the Pydantic schema from the backend style school generator.
5
+ * Used for typing API responses from GET /v1/styles/tokens/{schoolKey}.
6
+ */
7
+
8
+ export interface SemanticTriple {
9
+ bg: string;
10
+ text: string;
11
+ border: string;
12
+ }
13
+
14
+ export interface CategoricalItem {
15
+ bg: string;
16
+ text: string;
17
+ border: string;
18
+ label: string;
19
+ }
20
+
21
+ export interface PrimitiveTokens {
22
+ color_primary: string;
23
+ color_secondary: string;
24
+ color_tertiary: string;
25
+ color_accent: string;
26
+ color_accent_alt: string;
27
+ color_background: string;
28
+ color_highlight: string;
29
+ color_text: string;
30
+ color_muted: string;
31
+ color_positive: string;
32
+ color_negative: string;
33
+ series_palette: string[];
34
+ font_primary: string;
35
+ font_title: string;
36
+ font_caption: string;
37
+ font_number: string;
38
+ }
39
+
40
+ export interface SurfaceTokens {
41
+ surface_default: string;
42
+ surface_alt: string;
43
+ surface_elevated: string;
44
+ surface_inset: string;
45
+ border_default: string;
46
+ border_light: string;
47
+ border_accent: string;
48
+ text_default: string;
49
+ text_muted: string;
50
+ text_faint: string;
51
+ text_on_accent: string;
52
+ text_inverse: string;
53
+ shadow_sm: string;
54
+ shadow_md: string;
55
+ shadow_lg: string;
56
+ }
57
+
58
+ export interface ScaleTokens {
59
+ type_display: string;
60
+ type_heading: string;
61
+ type_subheading: string;
62
+ type_body: string;
63
+ type_caption: string;
64
+ type_label: string;
65
+ weight_light: string;
66
+ weight_regular: string;
67
+ weight_medium: string;
68
+ weight_semibold: string;
69
+ weight_bold: string;
70
+ weight_title: string;
71
+ leading_tight: string;
72
+ leading_normal: string;
73
+ leading_loose: string;
74
+ space_2xs: string;
75
+ space_xs: string;
76
+ space_sm: string;
77
+ space_md: string;
78
+ space_lg: string;
79
+ space_xl: string;
80
+ space_2xl: string;
81
+ space_3xl: string;
82
+ radius_sm: string;
83
+ radius_md: string;
84
+ radius_lg: string;
85
+ radius_xl: string;
86
+ radius_pill: string;
87
+ }
88
+
89
+ export interface SemanticTokens {
90
+ severity_high: SemanticTriple;
91
+ severity_medium: SemanticTriple;
92
+ severity_low: SemanticTriple;
93
+ visibility_explicit: SemanticTriple;
94
+ visibility_implicit: SemanticTriple;
95
+ visibility_hidden: SemanticTriple;
96
+ modality_ontological: SemanticTriple;
97
+ modality_methodological: SemanticTriple;
98
+ modality_normative: SemanticTriple;
99
+ modality_epistemic: SemanticTriple;
100
+ modality_causal: SemanticTriple;
101
+ change_stable: SemanticTriple;
102
+ change_narrowed: SemanticTriple;
103
+ change_expanded: SemanticTriple;
104
+ change_inverted: SemanticTriple;
105
+ change_metaphorized: SemanticTriple;
106
+ centrality_core: SemanticTriple;
107
+ centrality_supporting: SemanticTriple;
108
+ centrality_peripheral: SemanticTriple;
109
+ status_completed: SemanticTriple;
110
+ status_running: SemanticTriple;
111
+ status_pending: SemanticTriple;
112
+ status_failed: SemanticTriple;
113
+ }
114
+
115
+ export interface CategoricalTokens {
116
+ // Tactics (10)
117
+ tactic_conceptual_recycling: CategoricalItem;
118
+ tactic_silent_revision: CategoricalItem;
119
+ tactic_selective_continuity: CategoricalItem;
120
+ tactic_retroactive_framing: CategoricalItem;
121
+ tactic_escalation: CategoricalItem;
122
+ tactic_narrative_bootstrapping: CategoricalItem;
123
+ tactic_framework_migration: CategoricalItem;
124
+ tactic_condition_shift: CategoricalItem;
125
+ tactic_biographical_teleology: CategoricalItem;
126
+ tactic_strategic_amnesia: CategoricalItem;
127
+ // Forms (5)
128
+ form_proto_form: CategoricalItem;
129
+ form_full_form: CategoricalItem;
130
+ form_contradictory_form: CategoricalItem;
131
+ form_absent_but_implied: CategoricalItem;
132
+ form_different_framing: CategoricalItem;
133
+ // Idea types (11)
134
+ idea_central_thesis: CategoricalItem;
135
+ idea_supporting_argument: CategoricalItem;
136
+ idea_supporting_framework: CategoricalItem;
137
+ idea_methodological_tool: CategoricalItem;
138
+ idea_conceptual_framework: CategoricalItem;
139
+ idea_empirical_finding: CategoricalItem;
140
+ idea_normative_claim: CategoricalItem;
141
+ idea_analytical_distinction: CategoricalItem;
142
+ idea_rhetorical_strategy: CategoricalItem;
143
+ idea_rhetorical_device: CategoricalItem;
144
+ idea_historical_analysis: CategoricalItem;
145
+ // Condition types (8)
146
+ condition_conceptual_foundation: CategoricalItem;
147
+ condition_audience_preparation: CategoricalItem;
148
+ condition_authority_establishment: CategoricalItem;
149
+ condition_framework_provision: CategoricalItem;
150
+ condition_problem_definition: CategoricalItem;
151
+ condition_methodological_precedent: CategoricalItem;
152
+ condition_intellectual_toolkit: CategoricalItem;
153
+ condition_cross_domain_transfer: CategoricalItem;
154
+ // Relationship types (8)
155
+ relationship_direct_precursor: CategoricalItem;
156
+ relationship_methodological_ancestor: CategoricalItem;
157
+ relationship_counter_position: CategoricalItem;
158
+ relationship_indirect_contextualizer: CategoricalItem;
159
+ relationship_stylistic_influence: CategoricalItem;
160
+ relationship_conceptual_sibling: CategoricalItem;
161
+ relationship_different_field_relevant: CategoricalItem;
162
+ relationship_tangential: CategoricalItem;
163
+ // Strength (3)
164
+ strength_strong: CategoricalItem;
165
+ strength_moderate: CategoricalItem;
166
+ strength_weak: CategoricalItem;
167
+ // Awareness (3)
168
+ awareness_explicit: CategoricalItem;
169
+ awareness_implicit: CategoricalItem;
170
+ awareness_unconscious: CategoricalItem;
171
+ // Pattern types (7)
172
+ pattern_analytical_method: CategoricalItem;
173
+ pattern_cognitive_habit: CategoricalItem;
174
+ pattern_recurring_metaphor: CategoricalItem;
175
+ pattern_problem_solving_approach: CategoricalItem;
176
+ pattern_theoretical_framework: CategoricalItem;
177
+ pattern_argumentative_structure: CategoricalItem;
178
+ pattern_epistemic_commitment: CategoricalItem;
179
+ }
180
+
181
+ export interface ComponentTokens {
182
+ page_accent: string;
183
+ page_accent_hover: string;
184
+ page_accent_bg: string;
185
+ section_header_bg: string;
186
+ section_header_border: string;
187
+ section_header_text: string;
188
+ card_bg: string;
189
+ card_border: string;
190
+ card_border_accent: string;
191
+ card_header_bg: string;
192
+ card_header_text: string;
193
+ chip_weight_0_bg: string;
194
+ chip_weight_0_text: string;
195
+ chip_weight_0_border: string;
196
+ chip_weight_25_bg: string;
197
+ chip_weight_25_text: string;
198
+ chip_weight_25_border: string;
199
+ chip_weight_50_bg: string;
200
+ chip_weight_50_text: string;
201
+ chip_weight_50_border: string;
202
+ chip_weight_75_bg: string;
203
+ chip_weight_75_text: string;
204
+ chip_weight_75_border: string;
205
+ chip_weight_100_bg: string;
206
+ chip_weight_100_text: string;
207
+ chip_weight_100_border: string;
208
+ chip_header_bg: string;
209
+ chip_header_text: string;
210
+ prose_lede_color: string;
211
+ prose_lede_weight: string;
212
+ prose_blockquote_border: string;
213
+ prose_blockquote_bg: string;
214
+ timeline_connector: string;
215
+ timeline_connector_width: string;
216
+ timeline_node_bg: string;
217
+ timeline_node_border: string;
218
+ evidence_dot_bg: string;
219
+ evidence_connector: string;
220
+ stat_number_color: string;
221
+ stat_label_color: string;
222
+ stat_card_bg: string;
223
+ }
224
+
225
+ export interface DesignTokenSet {
226
+ school_key: string;
227
+ school_name: string;
228
+ generated_at: string;
229
+ version: string;
230
+ primitives: PrimitiveTokens;
231
+ surfaces: SurfaceTokens;
232
+ scales: ScaleTokens;
233
+ semantic: SemanticTokens;
234
+ categorical: CategoricalTokens;
235
+ components: ComponentTokens;
236
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Renderer type definitions — shared across all consumer apps.
3
+ */
4
+
5
+ import React from 'react';
6
+
7
+ /** The props every top-level renderer receives. */
8
+ export interface RendererProps {
9
+ /** The full analysis result data — renderers pick what they need */
10
+ data: unknown;
11
+ /** Renderer-specific configuration from the view definition */
12
+ config: Record<string, unknown>;
13
+ /** Children views (for container renderers like tabs) */
14
+ children?: React.ReactNode;
15
+ }
16
+
17
+ export type RendererComponent = React.ComponentType<RendererProps>;
18
+
19
+ /** The props every cell renderer receives (inside card grids). */
20
+ export interface CellRendererProps {
21
+ /** The individual item to render inside a card */
22
+ item: Record<string, unknown>;
23
+ /** The full renderer_config from the view definition */
24
+ config: Record<string, unknown>;
25
+ }
26
+
27
+ export type CellRendererComponent = React.ComponentType<CellRendererProps>;
28
+
29
+ /** The props every sub-renderer receives (inside accordion sections). */
30
+ export interface SubRendererProps {
31
+ data: unknown;
32
+ config: Record<string, unknown>;
33
+ }
34
+
35
+ export type SubRendererComponent = React.FC<SubRendererProps>;
36
+
37
+ // Re-export from sibling type files
38
+ export type {
39
+ StyleOverrides,
40
+ } from './styles';
41
+ export { getSO } from './styles';
42
+
43
+ export type {
44
+ DesignTokenSet,
45
+ SemanticTriple,
46
+ CategoricalItem,
47
+ PrimitiveTokens,
48
+ SurfaceTokens,
49
+ ScaleTokens,
50
+ SemanticTokens,
51
+ CategoricalTokens,
52
+ ComponentTokens,
53
+ } from './designTokens';
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Shared style override types for the renderer system.
3
+ *
4
+ * StyleOverrides is the contract between the polish system (analyzer-v2)
5
+ * and the frontend renderers. The polisher generates CSS property maps
6
+ * for each injection point; renderers apply them as inline styles.
7
+ *
8
+ * 24 injection points total: 10 existing + 14 fine-grained.
9
+ */
10
+
11
+ export interface StyleOverrides {
12
+ // === Existing injection points ===
13
+ section_header?: Record<string, string>;
14
+ section_content?: Record<string, string>;
15
+ card?: Record<string, string>;
16
+ chip?: Record<string, string>;
17
+ badge?: Record<string, string>;
18
+ timeline_node?: Record<string, string>;
19
+ prose?: Record<string, string>;
20
+ accent_color?: string;
21
+ view_wrapper?: Record<string, string>;
22
+ items_container?: Record<string, string>;
23
+
24
+ // === Fine-grained injection points ===
25
+ section_title?: Record<string, string>;
26
+ section_description?: Record<string, string>;
27
+ card_header?: Record<string, string>;
28
+ card_body?: Record<string, string>;
29
+ chip_label?: Record<string, string>;
30
+ chip_expanded?: Record<string, string>;
31
+ prose_lede?: Record<string, string>;
32
+ prose_body?: Record<string, string>;
33
+ prose_quote?: Record<string, string>;
34
+ timeline_connector?: Record<string, string>;
35
+ stat_number?: Record<string, string>;
36
+ stat_label?: Record<string, string>;
37
+ hero_card?: Record<string, string>;
38
+ view_header?: Record<string, string>;
39
+ }
40
+
41
+ /** Helper to extract StyleOverrides from a renderer config object. */
42
+ export function getSO(config: Record<string, unknown>): StyleOverrides | undefined {
43
+ return config._style_overrides as StyleOverrides | undefined;
44
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Token Flattener - Converts nested DesignTokenSet to flat CSS custom properties.
3
+ *
4
+ * Transforms the structured token hierarchy into a flat Record<string, string>
5
+ * where keys are CSS custom property names (--dt-*) and values are token values.
6
+ * This is injected into the DOM via element.style.setProperty() by DesignTokenContext.
7
+ */
8
+
9
+ import { DesignTokenSet, SemanticTriple, CategoricalItem } from '../types/designTokens';
10
+
11
+ /**
12
+ * Flattens a nested DesignTokenSet into a flat Record<string, string>
13
+ * where keys are CSS custom property names (--dt-*) and values are the token values.
14
+ */
15
+ export function flattenTokens(tokens: DesignTokenSet): Record<string, string> {
16
+ const result: Record<string, string> = {};
17
+
18
+ // Tier 1: Primitives (skip non-CSS values like series_palette array)
19
+ const primitives = tokens.primitives;
20
+ for (const [key, value] of Object.entries(primitives)) {
21
+ if (key === 'series_palette') {
22
+ // Store each series color individually
23
+ (value as string[]).forEach((color, i) => {
24
+ result[`--dt-series-${i}`] = color;
25
+ });
26
+ } else {
27
+ result[`--dt-${key.replace(/_/g, '-')}`] = value as string;
28
+ }
29
+ }
30
+
31
+ // Tier 2: Surfaces
32
+ for (const [key, value] of Object.entries(tokens.surfaces)) {
33
+ result[`--dt-${key.replace(/_/g, '-')}`] = value as string;
34
+ }
35
+
36
+ // Tier 3: Scales
37
+ for (const [key, value] of Object.entries(tokens.scales)) {
38
+ result[`--dt-${key.replace(/_/g, '-')}`] = value as string;
39
+ }
40
+
41
+ // Tier 4: Semantic (each is a SemanticTriple with bg, text, border)
42
+ for (const [key, triple] of Object.entries(tokens.semantic)) {
43
+ const t = triple as SemanticTriple;
44
+ result[`--dt-${key.replace(/_/g, '-')}-bg`] = t.bg;
45
+ result[`--dt-${key.replace(/_/g, '-')}-text`] = t.text;
46
+ result[`--dt-${key.replace(/_/g, '-')}-border`] = t.border;
47
+ }
48
+
49
+ // Tier 5: Categorical (each is a CategoricalItem with bg, text, border, label)
50
+ for (const [key, item] of Object.entries(tokens.categorical)) {
51
+ const c = item as CategoricalItem;
52
+ result[`--dt-${key.replace(/_/g, '-')}-bg`] = c.bg;
53
+ result[`--dt-${key.replace(/_/g, '-')}-text`] = c.text;
54
+ result[`--dt-${key.replace(/_/g, '-')}-border`] = c.border;
55
+ // Labels are not CSS properties - they're accessed via JS context
56
+ }
57
+
58
+ // Tier 6: Components
59
+ for (const [key, value] of Object.entries(tokens.components)) {
60
+ result[`--dt-${key.replace(/_/g, '-')}`] = value as string;
61
+ }
62
+
63
+ return result;
64
+ }