claude-ws 0.3.97 → 0.3.99

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 (86) hide show
  1. package/locales/de.json +374 -12
  2. package/locales/en.json +374 -12
  3. package/locales/es.json +398 -11
  4. package/locales/fr.json +398 -11
  5. package/locales/ja.json +398 -11
  6. package/locales/ko.json +398 -11
  7. package/locales/vi.json +374 -12
  8. package/locales/zh.json +398 -11
  9. package/package.json +1 -1
  10. package/server.ts +283 -6
  11. package/src/app/[locale]/not-found.tsx +6 -3
  12. package/src/app/[locale]/page.tsx +14 -4
  13. package/src/app/api/attempts/[id]/workflow/route.ts +76 -0
  14. package/src/app/api/questions/answer/route.ts +58 -0
  15. package/src/app/api/questions/route.ts +68 -0
  16. package/src/app/api/tasks/[id]/compact/route.ts +62 -0
  17. package/src/components/access-anywhere/api-access-key-setup-modal.tsx +2 -2
  18. package/src/components/access-anywhere/tunnel-settings-dialog.tsx +6 -6
  19. package/src/components/access-anywhere/wizard-step-ctunnel.tsx +8 -8
  20. package/src/components/agent-factory/dependency-tree.tsx +5 -3
  21. package/src/components/agent-factory/discovery-dialog.tsx +26 -22
  22. package/src/components/agent-factory/plugin-detail-dialog.tsx +41 -38
  23. package/src/components/agent-factory/plugin-form-dialog.tsx +23 -20
  24. package/src/components/agent-factory/plugin-list.tsx +20 -17
  25. package/src/components/agent-factory/upload-dialog.tsx +17 -14
  26. package/src/components/auth/agent-provider-dialog.tsx +67 -65
  27. package/src/components/auth/api-key-dialog.tsx +14 -11
  28. package/src/components/auth/auth-error-message.tsx +6 -3
  29. package/src/components/editor/code-editor-with-inline-edit.tsx +4 -2
  30. package/src/components/editor/file-diff-resolver-modal.tsx +31 -26
  31. package/src/components/editor/inline-edit-dialog.tsx +9 -6
  32. package/src/components/editor/selection-mention-popup.tsx +3 -1
  33. package/src/components/header/project-selector.tsx +7 -4
  34. package/src/components/header.tsx +70 -4
  35. package/src/components/kanban/column.tsx +11 -0
  36. package/src/components/kanban/task-card.tsx +70 -4
  37. package/src/components/project-settings/component-selector.tsx +3 -1
  38. package/src/components/project-settings/plugin-upload-dialog.tsx +7 -5
  39. package/src/components/project-settings/project-settings-dialog.tsx +5 -3
  40. package/src/components/questions/questions-panel.tsx +136 -0
  41. package/src/components/settings/folder-browser-dialog.tsx +29 -25
  42. package/src/components/settings/settings-page.tsx +64 -18
  43. package/src/components/settings/setup-dialog.tsx +26 -23
  44. package/src/components/setup/unified-setup-wizard.tsx +12 -9
  45. package/src/components/sidebar/file-browser/file-create-buttons.tsx +7 -3
  46. package/src/components/sidebar/file-browser/file-tab-content.tsx +19 -15
  47. package/src/components/sidebar/file-browser/file-tabs-panel.tsx +7 -4
  48. package/src/components/sidebar/file-browser/file-tree.tsx +3 -1
  49. package/src/components/sidebar/git-changes/branch-checkout-modal.tsx +6 -4
  50. package/src/components/sidebar/git-changes/commit-details-modal.tsx +5 -3
  51. package/src/components/sidebar/git-changes/diff-tabs-panel.tsx +3 -1
  52. package/src/components/sidebar/git-changes/git-file-item.tsx +8 -6
  53. package/src/components/sidebar/git-changes/git-graph.tsx +8 -5
  54. package/src/components/sidebar/git-changes/git-panel.tsx +28 -27
  55. package/src/components/sidebar/git-changes/git-section.tsx +5 -3
  56. package/src/components/sidebar/shells/shell-panel.tsx +3 -1
  57. package/src/components/task/attachment-bar.tsx +4 -1
  58. package/src/components/task/attempt-item.tsx +7 -5
  59. package/src/components/task/conversation-view.tsx +21 -13
  60. package/src/components/task/floating-chat-window.tsx +14 -5
  61. package/src/components/task/interactive-command/checkpoint-list.tsx +5 -3
  62. package/src/components/task/interactive-command/confirm-dialog.tsx +9 -4
  63. package/src/components/task/interactive-command/interactive-command-overlay.tsx +23 -9
  64. package/src/components/task/interactive-command/question-prompt.tsx +12 -8
  65. package/src/components/task/pending-question-indicator.tsx +5 -3
  66. package/src/components/task/prompt-input.tsx +1 -1
  67. package/src/components/task/shell-log-view.tsx +3 -1
  68. package/src/components/task/status-line.tsx +84 -23
  69. package/src/components/task/task-detail-panel.tsx +27 -27
  70. package/src/components/task/task-shell-indicator.tsx +10 -6
  71. package/src/components/terminal/terminal-context-menu.tsx +6 -4
  72. package/src/components/terminal/terminal-instance.tsx +11 -3
  73. package/src/components/terminal/terminal-panel.tsx +6 -3
  74. package/src/components/terminal/terminal-shortcut-bar.tsx +3 -1
  75. package/src/components/terminal/terminal-tab-bar.tsx +5 -3
  76. package/src/components/workflow/workflow-panel.tsx +181 -0
  77. package/src/hooks/use-attempt-stream.ts +96 -3
  78. package/src/lib/agent-manager.ts +89 -3
  79. package/src/lib/db/index.ts +18 -0
  80. package/src/lib/db/schema.ts +29 -0
  81. package/src/lib/process-manager.ts +28 -7
  82. package/src/lib/session-manager.ts +60 -0
  83. package/src/lib/usage-tracker.ts +19 -19
  84. package/src/lib/workflow-tracker.ts +118 -20
  85. package/src/stores/questions-store.ts +76 -0
  86. package/src/stores/workflow-store.ts +71 -0
@@ -0,0 +1,62 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { db, schema } from '@/lib/db';
3
+ import { eq } from 'drizzle-orm';
4
+ import { nanoid } from 'nanoid';
5
+ import { agentManager } from '@/lib/agent-manager';
6
+ import { sessionManager } from '@/lib/session-manager';
7
+ import { createLogger } from '@/lib/logger';
8
+
9
+ const log = createLogger('CompactTask');
10
+
11
+ // POST /api/tasks/[id]/compact - Trigger conversation compaction
12
+ export async function POST(
13
+ request: NextRequest,
14
+ { params }: { params: Promise<{ id: string }> }
15
+ ) {
16
+ try {
17
+ const { id: taskId } = await params;
18
+
19
+ const task = await db.query.tasks.findFirst({
20
+ where: eq(schema.tasks.id, taskId),
21
+ });
22
+
23
+ if (!task) {
24
+ return NextResponse.json({ error: 'Task not found' }, { status: 404 });
25
+ }
26
+
27
+ const project = await db.query.projects.findFirst({
28
+ where: eq(schema.projects.id, task.projectId),
29
+ });
30
+
31
+ if (!project) {
32
+ return NextResponse.json({ error: 'Project not found' }, { status: 404 });
33
+ }
34
+
35
+ const conversationSummary = await sessionManager.getConversationSummary(taskId);
36
+
37
+ const attemptId = nanoid();
38
+ await db.insert(schema.attempts).values({
39
+ id: attemptId,
40
+ taskId,
41
+ prompt: 'Compact: summarize conversation context',
42
+ displayPrompt: 'Compacting conversation...',
43
+ status: 'running',
44
+ });
45
+
46
+ log.info({ attemptId, taskId }, 'Starting compact');
47
+
48
+ agentManager.compact({
49
+ attemptId,
50
+ projectPath: project.path,
51
+ conversationSummary,
52
+ });
53
+
54
+ return NextResponse.json({ success: true, attemptId });
55
+ } catch (error) {
56
+ log.error({ error }, 'Failed to compact');
57
+ return NextResponse.json(
58
+ { error: 'Failed to compact conversation' },
59
+ { status: 500 }
60
+ );
61
+ }
62
+ }
@@ -73,14 +73,14 @@ export function ApiAccessKeySetupForm({ onSuccess }: ApiAccessKeySetupFormProps)
73
73
  const data = await res.json();
74
74
 
75
75
  if (!res.ok) {
76
- throw new Error(data.error || 'Failed to save API key');
76
+ throw new Error(data.error || t('failedToSaveApiKey'));
77
77
  }
78
78
 
79
79
  if (onSuccess) {
80
80
  onSuccess();
81
81
  }
82
82
  } catch (err) {
83
- setError(err instanceof Error ? err.message : 'Failed to save API key');
83
+ setError(err instanceof Error ? err.message : t('failedToSaveApiKey'));
84
84
  } finally {
85
85
  setSaving(false);
86
86
  }
@@ -81,7 +81,7 @@ export function TunnelSettingsDialog() {
81
81
  };
82
82
 
83
83
  const handleReset = async () => {
84
- if (confirm('Are you sure you want to reset the tunnel configuration? This will clear all saved settings and open the setup wizard again.')) {
84
+ if (confirm(t('resetConfirmation'))) {
85
85
  setResetting(true);
86
86
  try {
87
87
  await useTunnelStore.getState().resetOnboarding();
@@ -120,7 +120,7 @@ export function TunnelSettingsDialog() {
120
120
  <VisuallyHidden>
121
121
  <DialogTitle>Access Anywhere</DialogTitle>
122
122
  </VisuallyHidden>
123
- No tunnel configuration found. Please set up Access Anywhere first.
123
+ {t('noTunnelConfig')}
124
124
  </div>
125
125
  ) : (
126
126
  <>
@@ -141,7 +141,7 @@ export function TunnelSettingsDialog() {
141
141
  variant={status === 'connected' ? 'default' : 'secondary'}
142
142
  className={status === 'connected' ? 'bg-green-500 hover:bg-green-600 text-white' : ''}
143
143
  >
144
- {status === 'connected' ? '● Connected' : '○ Disconnected'}
144
+ {status === 'connected' ? `● ${t('connected')}` : `○ ${t('disconnected')}`}
145
145
  </Badge>
146
146
  {subdomain && (
147
147
  <>
@@ -159,7 +159,7 @@ export function TunnelSettingsDialog() {
159
159
  variant="outline"
160
160
  size="sm"
161
161
  onClick={handleCopyUrl}
162
- title={copiedUrl ? 'Copied!' : 'Copy URL'}
162
+ title={copiedUrl ? t('copiedToClipboard') : t('copyUrl')}
163
163
  >
164
164
  {copiedUrl ? <Check className="h-3 w-3 text-green-500" /> : <Copy className="h-3 w-3" />}
165
165
  </Button>
@@ -195,13 +195,13 @@ export function TunnelSettingsDialog() {
195
195
  variant="outline"
196
196
  size="icon"
197
197
  onClick={handleCopyApiKey}
198
- title={copied ? 'Copied!' : 'Copy API key'}
198
+ title={copied ? t('copiedToClipboard') : t('clickToCopyApiKey')}
199
199
  >
200
200
  {copied ? <RefreshCw className="h-4 w-4 text-green-500" /> : <Copy className="h-4 w-4" />}
201
201
  </Button>
202
202
  </div>
203
203
  <p className="text-xs text-muted-foreground">
204
- {copied ? 'Copied to clipboard!' : 'Click to copy full API key'}
204
+ {copied ? t('copiedToClipboard') : t('clickToCopyApiKey')}
205
205
  </p>
206
206
  </div>
207
207
  )}
@@ -73,7 +73,7 @@ export function WizardStepCtunnel() {
73
73
 
74
74
  const checkAvailability = async () => {
75
75
  if (!subdomain || !email) {
76
- setErrorMessage('Subdomain and email are required');
76
+ setErrorMessage(t('subdomainRequired'));
77
77
  return;
78
78
  }
79
79
 
@@ -84,7 +84,7 @@ export function WizardStepCtunnel() {
84
84
  // Subdomain already registered to this email, proceed to confirmation or direct connect
85
85
  await registerSubdomain();
86
86
  } else {
87
- setErrorMessage('Subdomain is not available');
87
+ setErrorMessage(t('subdomainNotAvailable'));
88
88
  }
89
89
  return;
90
90
  }
@@ -107,10 +107,10 @@ export function WizardStepCtunnel() {
107
107
  if (data.success) {
108
108
  setStep('confirm');
109
109
  } else {
110
- setErrorMessage(data.message || 'Failed to register subdomain');
110
+ setErrorMessage(data.message || t('failedToRegisterSubdomain'));
111
111
  }
112
112
  } catch (error) {
113
- setErrorMessage(error instanceof Error ? error.message : 'Failed to register subdomain');
113
+ setErrorMessage(error instanceof Error ? error.message : t('failedToRegisterSubdomain'));
114
114
  } finally {
115
115
  setRegistering(false);
116
116
  }
@@ -118,7 +118,7 @@ export function WizardStepCtunnel() {
118
118
 
119
119
  const confirmSubdomain = async () => {
120
120
  if (!confirmationCode) {
121
- setErrorMessage('Confirmation code is required');
121
+ setErrorMessage(t('confirmationCodeIsRequired'));
122
122
  return;
123
123
  }
124
124
 
@@ -137,10 +137,10 @@ export function WizardStepCtunnel() {
137
137
  setStep('connecting');
138
138
  await startTunnel(subdomain);
139
139
  } else {
140
- setErrorMessage(data.message || 'Failed to confirm subdomain');
140
+ setErrorMessage(data.message || t('failedToConfirmSubdomain'));
141
141
  }
142
142
  } catch (error) {
143
- setErrorMessage(error instanceof Error ? error.message : 'Failed to confirm subdomain');
143
+ setErrorMessage(error instanceof Error ? error.message : t('failedToConfirmSubdomain'));
144
144
  } finally {
145
145
  setVerifying(false);
146
146
  }
@@ -151,7 +151,7 @@ export function WizardStepCtunnel() {
151
151
  setStep('connected');
152
152
  } else if (status === 'error' && step === 'connecting') {
153
153
  setStep('error');
154
- setErrorMessage(error || 'Failed to start tunnel');
154
+ setErrorMessage(error || t('failedToStartTunnel'));
155
155
  }
156
156
  }, [status, error]);
157
157
 
@@ -3,6 +3,7 @@
3
3
  import { Badge } from '@/components/ui/badge';
4
4
  import { ChevronRight, ChevronDown, AlertTriangle, AlertCircle } from 'lucide-react';
5
5
  import { useState } from 'react';
6
+ import { useTranslations } from 'next-intl';
6
7
 
7
8
  export interface DependencyTreeNode {
8
9
  type: 'skill' | 'command' | 'agent';
@@ -29,6 +30,7 @@ export function DependencyTree({ nodes }: DependencyTreeProps) {
29
30
  }
30
31
 
31
32
  function TreeNode({ node }: { node: DependencyTreeNode }) {
33
+ const t = useTranslations('agentFactory');
32
34
  const [expanded, setExpanded] = useState(true);
33
35
  const hasChildren = node.children && node.children.length > 0;
34
36
 
@@ -68,20 +70,20 @@ function TreeNode({ node }: { node: DependencyTreeNode }) {
68
70
  {node.cycle && (
69
71
  <Badge variant="outline" className="text-orange-500 border-orange-500 text-xs">
70
72
  <AlertTriangle className="w-3 h-3 mr-1" />
71
- Cycle
73
+ {t('cycle')}
72
74
  </Badge>
73
75
  )}
74
76
 
75
77
  {node.missing && (
76
78
  <Badge variant="outline" className="text-red-500 border-red-500 text-xs">
77
79
  <AlertCircle className="w-3 h-3 mr-1" />
78
- Missing
80
+ {t('missing')}
79
81
  </Badge>
80
82
  )}
81
83
 
82
84
  {node.truncated && (
83
85
  <Badge variant="outline" className="text-gray-500 text-xs">
84
- Max depth
86
+ {t('maxDepth')}
85
87
  </Badge>
86
88
  )}
87
89
 
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useMemo, useCallback, memo } from 'react';
4
+ import { useTranslations } from 'next-intl';
4
5
  import {
5
6
  Dialog,
6
7
  DialogContent,
@@ -102,6 +103,7 @@ const TreeNode = memo(function TreeNode({
102
103
  onImport,
103
104
  onClick
104
105
  }: TreeNodeProps) {
106
+ const t = useTranslations('agentFactory');
105
107
  const key = getNodeKey(node, index);
106
108
  const isExpanded = expandedFolders.has(key);
107
109
 
@@ -121,11 +123,11 @@ const TreeNode = memo(function TreeNode({
121
123
  const getStatusBadge = (status: string) => {
122
124
  switch (status) {
123
125
  case 'new':
124
- return <span className="text-xs px-2 py-0.5 rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">New</span>;
126
+ return <span className="text-xs px-2 py-0.5 rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200">{t('newStatus')}</span>;
125
127
  case 'update':
126
- return <span className="text-xs px-2 py-0.5 rounded-full bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200">Update</span>;
128
+ return <span className="text-xs px-2 py-0.5 rounded-full bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200">{t('update')}</span>;
127
129
  case 'current':
128
- return <span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200">Current</span>;
130
+ return <span className="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200">{t('current')}</span>;
129
131
  default:
130
132
  return null;
131
133
  }
@@ -253,10 +255,10 @@ const TreeNode = memo(function TreeNode({
253
255
  ) : statusMap.get(`${node.type}-${node.name}`)?.status === 'update' ? (
254
256
  <>
255
257
  <RotateCcw className="w-3 h-3 mr-1" />
256
- Update
258
+ {t('update')}
257
259
  </>
258
260
  ) : (
259
- 'Import'
261
+ t('import')
260
262
  )}
261
263
  </Button>
262
264
  </>
@@ -287,6 +289,8 @@ const TreeNode = memo(function TreeNode({
287
289
  });
288
290
 
289
291
  export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
292
+ const t = useTranslations('agentFactory');
293
+ const tCommon = useTranslations('common');
290
294
  const { plugins, discovering, discoverPlugins, importPlugin, fetchPlugins } = useAgentFactoryStore();
291
295
  const [discovered, setDiscovered] = useState<DiscoveredNode[]>([]);
292
296
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
@@ -550,10 +554,10 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
550
554
  <DialogHeader>
551
555
  <DialogTitle className="flex items-center gap-3">
552
556
  <Package className="w-6 h-6" />
553
- Discover Plugins
557
+ {t('discoverPlugins')}
554
558
  </DialogTitle>
555
559
  <DialogDescription>
556
- Scan your filesystem for existing Claude plugins and import them into Agent Factory.
560
+ {t('scanDescription')}
557
561
  </DialogDescription>
558
562
  </DialogHeader>
559
563
 
@@ -561,17 +565,17 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
561
565
  {!scanned ? (
562
566
  <div className="text-center py-12 text-muted-foreground">
563
567
  <Package className="w-12 h-12 mx-auto mb-4 text-muted-foreground/50" />
564
- <p className="mb-4">Click the Scan button to search for plugins</p>
568
+ <p className="mb-4">{t('clickScanToSearch')}</p>
565
569
  <Button onClick={handleScan} disabled={scanning}>
566
570
  {scanning ? (
567
571
  <>
568
572
  <RefreshCw className="w-4 h-4 mr-2 animate-spin" />
569
- Scanning...
573
+ {tCommon('scanning')}
570
574
  </>
571
575
  ) : (
572
576
  <>
573
577
  <Search className="w-4 h-4 mr-2" />
574
- Scan
578
+ {t('scan')}
575
579
  </>
576
580
  )}
577
581
  </Button>
@@ -579,11 +583,11 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
579
583
  ) : scanning ? (
580
584
  <div className="text-center py-8 text-muted-foreground">
581
585
  <div className="animate-spin w-8 h-8 border-2 border-primary border-t-transparent rounded-full mx-auto mb-4" />
582
- Scanning for plugins...
586
+ {t('scanningForPlugins')}
583
587
  </div>
584
588
  ) : discovered.length === 0 ? (
585
589
  <div className="text-center py-8 text-muted-foreground">
586
- <p className="mb-4">No plugins found /skills, /commands, or /agents</p>
590
+ <p className="mb-4">{t('noPluginsFoundScan')}</p>
587
591
  <Button variant="outline" onClick={handleScan} disabled={scanning}>
588
592
  <RefreshCw className="w-4 h-4 mr-2" />
589
593
  Rescan
@@ -592,19 +596,19 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
592
596
  ) : (
593
597
  <div className="space-y-2">
594
598
  <div className="flex items-center justify-between px-2 py-1 text-sm text-muted-foreground sticky top-0 bg-background">
595
- <span>{statusMap.size} plugins found</span>
599
+ <span>{statusMap.size} {t('pluginsFound')}</span>
596
600
  <div className="flex gap-2">
597
601
  <span className="flex items-center gap-1">
598
602
  <span className="w-2 h-2 rounded-full bg-blue-500"></span>
599
- {newCount} new
603
+ {newCount} {t('newStatus')}
600
604
  </span>
601
605
  <span className="flex items-center gap-1">
602
606
  <span className="w-2 h-2 rounded-full bg-orange-500"></span>
603
- {updateCount} updates
607
+ {updateCount} {t('updates')}
604
608
  </span>
605
609
  <span className="flex items-center gap-1">
606
610
  <span className="w-2 h-2 rounded-full bg-gray-400"></span>
607
- {currentCount} current
611
+ {currentCount} {t('current')}
608
612
  </span>
609
613
  </div>
610
614
  </div>
@@ -634,7 +638,7 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
634
638
  </span>
635
639
  <div className="flex gap-2">
636
640
  <Button variant="outline" onClick={() => onOpenChange(false)}>
637
- Close
641
+ {tCommon('close')}
638
642
  </Button>
639
643
  {scanned && discovered.length > 0 && (
640
644
  <>
@@ -644,7 +648,7 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
644
648
  disabled={scanning}
645
649
  >
646
650
  <RefreshCw className="w-4 h-4 mr-2" />
647
- Rescan
651
+ {t('rescan')}
648
652
  </Button>
649
653
  {needsAction > 0 && (
650
654
  <Button
@@ -654,11 +658,11 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
654
658
  {importing ? (
655
659
  <>
656
660
  <RefreshCw className="w-4 h-4 mr-2 animate-spin" />
657
- Importing...
661
+ {tCommon('importing')}
658
662
  </>
659
663
  ) : (
660
664
  <>
661
- Import All ({needsAction})
665
+ {t('importAll')} ({needsAction})
662
666
  </>
663
667
  )}
664
668
  </Button>
@@ -671,10 +675,10 @@ export function DiscoveryDialog({ open, onOpenChange }: DiscoveryDialogProps) {
671
675
  {importing ? (
672
676
  <>
673
677
  <RefreshCw className="w-4 h-4 mr-2 animate-spin" />
674
- Importing...
678
+ {tCommon('importing')}
675
679
  </>
676
680
  ) : (
677
- `Import ${selectedIds.size} Selected`
681
+ t('importSelected', { count: selectedIds.size })
678
682
  )}
679
683
  </Button>
680
684
  )}