@deepgram/styles 0.2.12 → 0.2.14

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.
@@ -0,0 +1,724 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for @deepgram/styles
5
+ *
6
+ * Exposes the design system (tokens, components, examples) as MCP tools
7
+ * so AI agents can query BEM classes, rendered HTML examples, and tokens.
8
+ *
9
+ * Usage:
10
+ * npx deepgram-styles-mcp # uses bundled design-system.yaml
11
+ * node dist/mcp/server.js --yaml /path/to/design-system.yaml
12
+ */
13
+
14
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
+ import { readFileSync } from "node:fs";
17
+ import { resolve, dirname } from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+ import { parseDocument } from "yaml";
20
+ import { z } from "zod";
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Inline types (subset of tools/yaml-pipeline/src/schema/types.ts)
24
+ // ---------------------------------------------------------------------------
25
+
26
+ interface CSSRule {
27
+ apply?: string;
28
+ properties?: Record<string, string>;
29
+ }
30
+
31
+ type CSSRuleMap = Record<string, CSSRule>;
32
+
33
+ interface StyleState {
34
+ apply?: string;
35
+ properties?: Record<string, string>;
36
+ }
37
+
38
+ interface ComponentStyles extends StyleState {
39
+ hover?: StyleState;
40
+ focus?: StyleState;
41
+ "focus-visible"?: StyleState;
42
+ "focus-within"?: StyleState;
43
+ active?: StyleState;
44
+ disabled?: StyleState;
45
+ checked?: StyleState;
46
+ "first-child"?: StyleState;
47
+ "last-child"?: StyleState;
48
+ media?: Record<string, StyleState>;
49
+ }
50
+
51
+ interface VariantNode {
52
+ title?: string;
53
+ description?: string;
54
+ styles?: ComponentStyles;
55
+ css?: CSSRuleMap;
56
+ media?: Record<string, CSSRuleMap>;
57
+ examples?: ComponentExample[];
58
+ }
59
+
60
+ interface ComponentNode {
61
+ $ref?: string;
62
+ tag?: string;
63
+ styles?: ComponentStyles;
64
+ variants?: Record<string, VariantNode>;
65
+ components?: Record<string, ComponentNode>;
66
+ css?: CSSRuleMap;
67
+ media?: Record<string, CSSRuleMap>;
68
+ examples?: ComponentExample[];
69
+ "default-props"?: Record<string, string>;
70
+ "class-props"?: Record<string, string>;
71
+ polymorphic?: boolean;
72
+ }
73
+
74
+ interface ASTNode {
75
+ tag: string;
76
+ props?: Record<string, string | boolean | number>;
77
+ children?: (ASTNode | UseRefNode | string)[];
78
+ text?: string;
79
+ }
80
+
81
+ interface UseRefNode {
82
+ $ref: string;
83
+ props?: Record<string, string | boolean | number>;
84
+ children?: (ASTNode | UseRefNode | string)[];
85
+ text?: string;
86
+ }
87
+
88
+ interface ComponentExample {
89
+ title: string;
90
+ description?: string;
91
+ ast: ASTNode | UseRefNode;
92
+ }
93
+
94
+ interface ComponentMetadata {
95
+ title: string;
96
+ description?: string;
97
+ category: string;
98
+ section: string;
99
+ subsection: string;
100
+ tags?: string[];
101
+ }
102
+
103
+ interface ComponentDefinition extends ComponentNode {
104
+ metadata: ComponentMetadata;
105
+ tag?: string;
106
+ variables?: Record<string, string>;
107
+ examples: ComponentExample[];
108
+ }
109
+
110
+ interface ColorToken {
111
+ value: string;
112
+ variable?: string;
113
+ fallback?: string;
114
+ }
115
+
116
+ interface FontToken {
117
+ family: string[];
118
+ "tailwind-key": string;
119
+ }
120
+
121
+ interface DesignTokens {
122
+ variables: Record<string, string>;
123
+ colors: {
124
+ brand: Record<string, ColorToken>;
125
+ background: Record<string, string>;
126
+ border: Record<string, string>;
127
+ text: Record<string, string>;
128
+ status: Record<string, string>;
129
+ gradient: Record<string, string>;
130
+ };
131
+ fonts: Record<string, FontToken>;
132
+ spacing: Record<string, string>;
133
+ "border-radius": Record<string, string>;
134
+ shadows: Record<string, string>;
135
+ }
136
+
137
+ interface DesignSystem {
138
+ version: string;
139
+ tokens: DesignTokens;
140
+ components: Record<string, ComponentDefinition>;
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Type guard
145
+ // ---------------------------------------------------------------------------
146
+
147
+ function isUseRefNode(node: unknown): node is UseRefNode {
148
+ return (
149
+ typeof node === "object" &&
150
+ node !== null &&
151
+ "$ref" in node &&
152
+ typeof (node as UseRefNode).$ref === "string" &&
153
+ !("tag" in node)
154
+ );
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Inline AST → HTML renderer (from generators/html.ts)
159
+ // ---------------------------------------------------------------------------
160
+
161
+ const SELF_CLOSING = new Set(["img", "br", "hr", "input", "meta", "link"]);
162
+
163
+ function formatHTMLProps(
164
+ props: Record<string, string | boolean | number>
165
+ ): string {
166
+ return Object.entries(props)
167
+ .map(([key, value]) => {
168
+ if (typeof value === "boolean") return value ? key : "";
169
+ const attrName = key === "className" ? "class" : key;
170
+ return `${attrName}="${value}"`;
171
+ })
172
+ .filter(Boolean)
173
+ .join(" ");
174
+ }
175
+
176
+ function indentStr(code: string, level = 1): string {
177
+ const spaces = " ".repeat(level);
178
+ return code
179
+ .split("\n")
180
+ .map((line) => (line ? spaces + line : line))
181
+ .join("\n");
182
+ }
183
+
184
+ function astToHTML(node: ASTNode, depth = 0): string {
185
+ const { tag, props = {}, children = [], text } = node;
186
+ const propsStr =
187
+ Object.keys(props).length > 0 ? " " + formatHTMLProps(props) : "";
188
+
189
+ if (SELF_CLOSING.has(tag) && !text && children.length === 0) {
190
+ return `<${tag}${propsStr} />`;
191
+ }
192
+
193
+ if (text) {
194
+ return `<${tag}${propsStr}>${text}</${tag}>`;
195
+ }
196
+
197
+ if (children.length > 0) {
198
+ const childrenStr = children
199
+ .map((child) => {
200
+ if (typeof child === "string") return child;
201
+ if ("tag" in child) return astToHTML(child, depth + 1);
202
+ return "";
203
+ })
204
+ .filter(Boolean)
205
+ .join("\n");
206
+
207
+ if (
208
+ children.length > 1 ||
209
+ (children[0] && typeof children[0] !== "string")
210
+ ) {
211
+ return `<${tag}${propsStr}>\n${indentStr(childrenStr, 1)}\n</${tag}>`;
212
+ }
213
+ return `<${tag}${propsStr}>${childrenStr}</${tag}>`;
214
+ }
215
+
216
+ return `<${tag}${propsStr}></${tag}>`;
217
+ }
218
+
219
+ // ---------------------------------------------------------------------------
220
+ // Inline $ref resolver (from parser/resolve-refs.ts)
221
+ // ---------------------------------------------------------------------------
222
+
223
+ function resolveComponentRefs(ds: DesignSystem): DesignSystem {
224
+ const resolved = { ...ds, components: { ...ds.components } };
225
+ for (const [key, component] of Object.entries(resolved.components)) {
226
+ resolved.components[key] = resolveComponentExamples(ds, component);
227
+ }
228
+ return resolved;
229
+ }
230
+
231
+ function resolveComponentExamples(
232
+ ds: DesignSystem,
233
+ component: ComponentDefinition
234
+ ): ComponentDefinition {
235
+ const result = { ...component };
236
+
237
+ if (result.examples) {
238
+ result.examples = result.examples.map((ex) => ({
239
+ ...ex,
240
+ ast: resolveNode(ds, ex.ast),
241
+ }));
242
+ }
243
+
244
+ if (result.variants) {
245
+ result.variants = { ...result.variants };
246
+ for (const [vKey, variant] of Object.entries(result.variants)) {
247
+ if (variant.examples) {
248
+ result.variants[vKey] = {
249
+ ...variant,
250
+ examples: variant.examples.map((ex) => ({
251
+ ...ex,
252
+ ast: resolveNode(ds, ex.ast),
253
+ })),
254
+ };
255
+ }
256
+ }
257
+ }
258
+
259
+ if (result.components) {
260
+ result.components = { ...result.components };
261
+ for (const [cKey, child] of Object.entries(result.components)) {
262
+ if (child.examples) {
263
+ result.components[cKey] = {
264
+ ...child,
265
+ examples: child.examples.map((ex) => ({
266
+ ...ex,
267
+ ast: resolveNode(ds, ex.ast),
268
+ })),
269
+ };
270
+ }
271
+ }
272
+ }
273
+
274
+ return result;
275
+ }
276
+
277
+ function resolveNode(ds: DesignSystem, node: ASTNode | UseRefNode): ASTNode {
278
+ if (isUseRefNode(node)) {
279
+ return resolveUseRef(ds, node);
280
+ }
281
+
282
+ if (!node.children) return node;
283
+
284
+ const resolvedChildren = node.children.map((child) => {
285
+ if (typeof child === "string") return child;
286
+ return resolveNode(ds, child);
287
+ });
288
+
289
+ return { ...node, children: resolvedChildren };
290
+ }
291
+
292
+ function resolveUseRef(ds: DesignSystem, ref: UseRefNode): ASTNode {
293
+ const segments = ref.$ref.split("/");
294
+ const componentKey = segments[0];
295
+
296
+ const component = ds.components[componentKey];
297
+ if (!component) {
298
+ return { tag: "div", props: { class: `dg-${componentKey}` }, text: `[unresolved: ${ref.$ref}]` };
299
+ }
300
+
301
+ const blockClass = `dg-${componentKey}`;
302
+ let tag = component.tag ?? "div";
303
+ const classes: string[] = [blockClass];
304
+
305
+ if (segments.length > 1) {
306
+ const pathSegment = segments.slice(1).join("/");
307
+ const resolved = resolvePathSegment(
308
+ ds,
309
+ component,
310
+ componentKey,
311
+ blockClass,
312
+ pathSegment
313
+ );
314
+ tag = resolved.tag;
315
+ classes.length = 0;
316
+ classes.push(...resolved.classes);
317
+ }
318
+
319
+ const mergedProps: Record<string, string | boolean | number> = {};
320
+ if (classes.length > 0) {
321
+ mergedProps.class = classes.join(" ");
322
+ }
323
+ if (ref.props) {
324
+ for (const [key, value] of Object.entries(ref.props)) {
325
+ if (key === "class" && typeof value === "string") {
326
+ mergedProps.class = mergedProps.class
327
+ ? `${mergedProps.class} ${value}`
328
+ : value;
329
+ } else {
330
+ mergedProps[key] = value;
331
+ }
332
+ }
333
+ }
334
+
335
+ let resolvedChildren: (ASTNode | string)[] | undefined;
336
+ if (ref.children) {
337
+ resolvedChildren = ref.children.map((child) => {
338
+ if (typeof child === "string") return child;
339
+ return resolveNode(ds, child);
340
+ });
341
+ }
342
+
343
+ const result: ASTNode = { tag, props: mergedProps };
344
+ if (resolvedChildren) result.children = resolvedChildren;
345
+ if (ref.text) result.text = ref.text;
346
+
347
+ return result;
348
+ }
349
+
350
+ interface ResolvedPath {
351
+ tag: string;
352
+ classes: string[];
353
+ }
354
+
355
+ function resolvePathSegment(
356
+ ds: DesignSystem,
357
+ component: ComponentDefinition,
358
+ componentKey: string,
359
+ blockClass: string,
360
+ pathSegment: string
361
+ ): ResolvedPath {
362
+ const parts = pathSegment.split("+");
363
+
364
+ if (parts.length > 1) {
365
+ for (const part of parts) {
366
+ if (!component.variants?.[part]) {
367
+ return {
368
+ tag: component.tag ?? "div",
369
+ classes: [blockClass, ...parts.map((p) => `${blockClass}--${p}`)],
370
+ };
371
+ }
372
+ }
373
+ return {
374
+ tag: component.tag ?? "div",
375
+ classes: [blockClass, ...parts.map((p) => `${blockClass}--${p}`)],
376
+ };
377
+ }
378
+
379
+ const name = parts[0];
380
+
381
+ if (component.variants?.[name]) {
382
+ return {
383
+ tag: component.tag ?? "div",
384
+ classes: [blockClass, `${blockClass}--${name}`],
385
+ };
386
+ }
387
+
388
+ if (component.components?.[name]) {
389
+ const child = component.components[name];
390
+ const elementClass = `dg-${componentKey}__${name}`;
391
+
392
+ if (child.$ref) {
393
+ const refComponent = ds.components[child.$ref];
394
+ return {
395
+ tag: child.tag ?? refComponent?.tag ?? "div",
396
+ classes: [`dg-${child.$ref}`, elementClass],
397
+ };
398
+ }
399
+
400
+ return { tag: child.tag ?? "div", classes: [elementClass] };
401
+ }
402
+
403
+ // Graceful fallback for unknown segments
404
+ return {
405
+ tag: component.tag ?? "div",
406
+ classes: [blockClass, `${blockClass}--${name}`],
407
+ };
408
+ }
409
+
410
+ // ---------------------------------------------------------------------------
411
+ // Inline example collector (from generators/collect-examples.ts)
412
+ // ---------------------------------------------------------------------------
413
+
414
+ interface CollectedExample {
415
+ example: ComponentExample;
416
+ variantKey?: string;
417
+ }
418
+
419
+ function collectAllExamples(
420
+ component: ComponentDefinition
421
+ ): CollectedExample[] {
422
+ const results: CollectedExample[] = [];
423
+
424
+ for (const example of component.examples) {
425
+ results.push({ example });
426
+ }
427
+
428
+ if (component.variants) {
429
+ for (const [vKey, variant] of Object.entries(component.variants)) {
430
+ if (variant.examples) {
431
+ for (const example of variant.examples) {
432
+ results.push({ example, variantKey: vKey });
433
+ }
434
+ }
435
+ }
436
+ }
437
+
438
+ return results;
439
+ }
440
+
441
+ // ---------------------------------------------------------------------------
442
+ // CSS class extractor
443
+ // ---------------------------------------------------------------------------
444
+
445
+ function extractBEMClasses(component: ComponentDefinition, name: string): string[] {
446
+ const classes = new Set<string>();
447
+ const blockClass = `dg-${name}`;
448
+ classes.add(blockClass);
449
+
450
+ // From css rules
451
+ if (component.css) {
452
+ for (const selector of Object.keys(component.css)) {
453
+ const matches = selector.match(/\.dg-[\w-]+(?:__[\w-]+)?(?:--[\w-]+)?/g);
454
+ if (matches) matches.forEach((m) => classes.add(m.replace(/^\./, "")));
455
+ }
456
+ }
457
+
458
+ // From variants
459
+ if (component.variants) {
460
+ for (const vKey of Object.keys(component.variants)) {
461
+ classes.add(`${blockClass}--${vKey}`);
462
+ const variant = component.variants[vKey];
463
+ if (variant.css) {
464
+ for (const selector of Object.keys(variant.css)) {
465
+ const matches = selector.match(/\.dg-[\w-]+(?:__[\w-]+)?(?:--[\w-]+)?/g);
466
+ if (matches) matches.forEach((m) => classes.add(m.replace(/^\./, "")));
467
+ }
468
+ }
469
+ }
470
+ }
471
+
472
+ // From elements/components
473
+ if (component.components) {
474
+ for (const cKey of Object.keys(component.components)) {
475
+ classes.add(`${blockClass}__${cKey}`);
476
+ }
477
+ }
478
+
479
+ // From elements (legacy css-based)
480
+ if ((component as any).elements) {
481
+ for (const eKey of Object.keys((component as any).elements)) {
482
+ classes.add(`${blockClass}__${eKey}`);
483
+ }
484
+ }
485
+
486
+ return [...classes].sort();
487
+ }
488
+
489
+ // ---------------------------------------------------------------------------
490
+ // Load YAML
491
+ // ---------------------------------------------------------------------------
492
+
493
+ function loadDesignSystem(yamlPath?: string): DesignSystem {
494
+ const __dirname = dirname(fileURLToPath(import.meta.url));
495
+ const resolvedPath = yamlPath
496
+ ? resolve(yamlPath)
497
+ : resolve(__dirname, "..", "..", "design-system.yaml");
498
+
499
+ const raw = readFileSync(resolvedPath, "utf-8");
500
+ const doc = parseDocument(raw);
501
+ const ds = doc.toJSON() as DesignSystem;
502
+
503
+ return resolveComponentRefs(ds);
504
+ }
505
+
506
+ // ---------------------------------------------------------------------------
507
+ // MCP Server
508
+ // ---------------------------------------------------------------------------
509
+
510
+ function createServer(ds: DesignSystem): McpServer {
511
+ const server = new McpServer({
512
+ name: "deepgram-styles",
513
+ version: ds.version ?? "0.0.0",
514
+ });
515
+
516
+ // ---- list_components ----
517
+ server.tool(
518
+ "list_components",
519
+ "List all components in the Deepgram design system. Returns component names, titles, categories, tags, and counts of variants and examples. Use the optional category filter to narrow results.",
520
+ { category: z.string().optional().describe("Filter by category (e.g. 'application-ui', 'marketing', 'documentation')") },
521
+ async ({ category }) => {
522
+ const entries = Object.entries(ds.components)
523
+ .filter(([, comp]) => !category || comp.metadata.category === category)
524
+ .map(([name, comp]) => ({
525
+ name,
526
+ title: comp.metadata.title,
527
+ category: comp.metadata.category,
528
+ section: comp.metadata.section,
529
+ subsection: comp.metadata.subsection,
530
+ tags: comp.metadata.tags ?? [],
531
+ variantCount: comp.variants ? Object.keys(comp.variants).length : 0,
532
+ exampleCount: collectAllExamples(comp).length,
533
+ }));
534
+
535
+ return {
536
+ content: [
537
+ {
538
+ type: "text" as const,
539
+ text: JSON.stringify(entries, null, 2),
540
+ },
541
+ ],
542
+ };
543
+ }
544
+ );
545
+
546
+ // ---- get_component ----
547
+ server.tool(
548
+ "get_component",
549
+ "Get full details for a specific Deepgram design system component. Returns BEM CSS classes, variant names, and rendered HTML examples you can copy-paste. Use list_components first to discover available component names.",
550
+ { name: z.string().describe("Component key (e.g. 'btn', 'card', 'alert')") },
551
+ async ({ name }) => {
552
+ const component = ds.components[name];
553
+ if (!component) {
554
+ return {
555
+ content: [
556
+ {
557
+ type: "text" as const,
558
+ text: `Component "${name}" not found. Use list_components to see available components.`,
559
+ },
560
+ ],
561
+ };
562
+ }
563
+
564
+ const bemClasses = extractBEMClasses(component, name);
565
+ const variants = component.variants
566
+ ? Object.entries(component.variants).map(([vKey, v]) => ({
567
+ name: vKey,
568
+ class: `dg-${name}--${vKey}`,
569
+ title: v.title,
570
+ description: v.description,
571
+ }))
572
+ : [];
573
+
574
+ const examples = collectAllExamples(component).map(
575
+ ({ example, variantKey }) => ({
576
+ title: example.title,
577
+ description: example.description,
578
+ variant: variantKey,
579
+ html: astToHTML(example.ast as ASTNode),
580
+ })
581
+ );
582
+
583
+ const result = {
584
+ name,
585
+ title: component.metadata.title,
586
+ description: component.metadata.description,
587
+ category: component.metadata.category,
588
+ section: component.metadata.section,
589
+ subsection: component.metadata.subsection,
590
+ tags: component.metadata.tags ?? [],
591
+ tag: component.tag ?? "div",
592
+ bemClasses,
593
+ variants,
594
+ examples,
595
+ };
596
+
597
+ return {
598
+ content: [
599
+ {
600
+ type: "text" as const,
601
+ text: JSON.stringify(result, null, 2),
602
+ },
603
+ ],
604
+ };
605
+ }
606
+ );
607
+
608
+ // ---- get_design_tokens ----
609
+ server.tool(
610
+ "get_design_tokens",
611
+ "Get design tokens (colors, spacing, fonts, shadows, border-radius, CSS variables) from the Deepgram design system. Tokens define the visual language — use these values instead of hardcoded colors or sizes.",
612
+ { group: z.string().optional().describe("Token group to return: 'colors', 'spacing', 'fonts', 'shadows', 'border-radius', 'variables'. Omit for all.") },
613
+ async ({ group }) => {
614
+ const tokens = ds.tokens;
615
+
616
+ if (group) {
617
+ const key = group as keyof DesignTokens;
618
+ if (!(key in tokens)) {
619
+ return {
620
+ content: [
621
+ {
622
+ type: "text" as const,
623
+ text: `Token group "${group}" not found. Available groups: ${Object.keys(tokens).join(", ")}`,
624
+ },
625
+ ],
626
+ };
627
+ }
628
+ return {
629
+ content: [
630
+ {
631
+ type: "text" as const,
632
+ text: JSON.stringify({ [key]: tokens[key] }, null, 2),
633
+ },
634
+ ],
635
+ };
636
+ }
637
+
638
+ return {
639
+ content: [
640
+ {
641
+ type: "text" as const,
642
+ text: JSON.stringify(tokens, null, 2),
643
+ },
644
+ ],
645
+ };
646
+ }
647
+ );
648
+
649
+ // ---- search_components ----
650
+ server.tool(
651
+ "search_components",
652
+ "Search for Deepgram design system components by keyword. Matches against component name, title, tags, description, category, section, and subsection. Returns matching components with metadata.",
653
+ { query: z.string().describe("Search keyword (e.g. 'button', 'navigation', 'form', 'card')") },
654
+ async ({ query }) => {
655
+ const q = query.toLowerCase();
656
+
657
+ const matches = Object.entries(ds.components)
658
+ .filter(([name, comp]) => {
659
+ const searchable = [
660
+ name,
661
+ comp.metadata.title,
662
+ comp.metadata.description ?? "",
663
+ comp.metadata.category,
664
+ comp.metadata.section,
665
+ comp.metadata.subsection,
666
+ ...(comp.metadata.tags ?? []),
667
+ ]
668
+ .join(" ")
669
+ .toLowerCase();
670
+ return searchable.includes(q);
671
+ })
672
+ .map(([name, comp]) => ({
673
+ name,
674
+ title: comp.metadata.title,
675
+ category: comp.metadata.category,
676
+ section: comp.metadata.section,
677
+ tags: comp.metadata.tags ?? [],
678
+ description: comp.metadata.description,
679
+ variantCount: comp.variants ? Object.keys(comp.variants).length : 0,
680
+ exampleCount: collectAllExamples(comp).length,
681
+ }));
682
+
683
+ return {
684
+ content: [
685
+ {
686
+ type: "text" as const,
687
+ text:
688
+ matches.length > 0
689
+ ? JSON.stringify(matches, null, 2)
690
+ : `No components found matching "${query}". Try list_components to see all available components.`,
691
+ },
692
+ ],
693
+ };
694
+ }
695
+ );
696
+
697
+ return server;
698
+ }
699
+
700
+ // ---------------------------------------------------------------------------
701
+ // CLI entry point
702
+ // ---------------------------------------------------------------------------
703
+
704
+ async function main(): Promise<void> {
705
+ // Parse --yaml flag
706
+ let yamlPath: string | undefined;
707
+ const args = process.argv.slice(2);
708
+ for (let i = 0; i < args.length; i++) {
709
+ if (args[i] === "--yaml" && args[i + 1]) {
710
+ yamlPath = args[i + 1];
711
+ i++;
712
+ }
713
+ }
714
+
715
+ const ds = loadDesignSystem(yamlPath);
716
+ const server = createServer(ds);
717
+ const transport = new StdioServerTransport();
718
+ await server.connect(transport);
719
+ }
720
+
721
+ main().catch((err) => {
722
+ console.error("Fatal:", err);
723
+ process.exit(1);
724
+ });