@promptbook/cli 0.112.0-126 → 0.112.0-127

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 (190) hide show
  1. package/README.md +5 -5
  2. package/apps/agents-server/README.md +3 -0
  3. package/apps/agents-server/playwright.config.ts +1 -0
  4. package/apps/agents-server/public/promptbook-logo-blue.png +0 -0
  5. package/apps/agents-server/public/promptbook-logo-white.png +0 -0
  6. package/apps/agents-server/src/app/[agentName]/layout.tsx +1 -0
  7. package/apps/agents-server/src/app/actions.ts +2 -2
  8. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +68 -36
  9. package/apps/agents-server/src/app/agents/[agentName]/ActiveAgentBreadcrumbBinder.tsx +30 -0
  10. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +2 -0
  11. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistoryPayloadState.ts +38 -4
  12. package/apps/agents-server/src/app/agents/[agentName]/chat/AgentChatHistorySyncOperations.ts +4 -1
  13. package/apps/agents-server/src/app/agents/[agentName]/chat/CanonicalAgentChatSurface.tsx +19 -15
  14. package/apps/agents-server/src/app/agents/[agentName]/chat/useCanonicalAgentChatPanelState.ts +58 -7
  15. package/apps/agents-server/src/app/agents/[agentName]/layout.tsx +47 -1
  16. package/apps/agents-server/src/app/api/chat/citation-label/route.ts +133 -0
  17. package/apps/agents-server/src/app/globals.css +78 -0
  18. package/apps/agents-server/src/components/Header/ActiveAgentBreadcrumbContext.tsx +82 -0
  19. package/apps/agents-server/src/components/Header/useHeaderActiveAgent.ts +26 -7
  20. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +35 -32
  21. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/ManGoNewAgentWizard.tsx +151 -0
  22. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/ManGoOnboardingNavigation.tsx +50 -0
  23. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/BookLanguagePanel.tsx +125 -0
  24. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/DropZone.tsx +83 -0
  25. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/EmailTestRun.tsx +216 -0
  26. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/KnowledgeList.tsx +79 -0
  27. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/Logo.tsx +20 -0
  28. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownBookEditor.tsx +154 -0
  29. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/MarkdownPreview.tsx +188 -0
  30. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/RailStepper.tsx +115 -0
  31. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/StepFrame.tsx +47 -0
  32. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/TestChat.tsx +159 -0
  33. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/WizardShell.tsx +114 -0
  34. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/brand/RailArtwork.tsx +60 -0
  35. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/BookStep.tsx +135 -0
  36. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/DoneStep.tsx +215 -0
  37. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/KnowledgeStep.tsx +152 -0
  38. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/TestStep.tsx +262 -0
  39. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/steps/ZadaniStep.tsx +64 -0
  40. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Badge.tsx +41 -0
  41. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Banner.tsx +69 -0
  42. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Button.tsx +77 -0
  43. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Card.tsx +33 -0
  44. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Field.tsx +103 -0
  45. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/IconButton.tsx +40 -0
  46. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/Spinner.tsx +14 -0
  47. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/components/ui/tokens.ts +37 -0
  48. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/bookSections.ts +62 -0
  49. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/emailScenarios.ts +22 -0
  50. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/config/steps.ts +46 -0
  51. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/index.ts +11 -0
  52. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/cn.ts +4 -0
  53. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/format.ts +9 -0
  54. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/lib/id.ts +11 -0
  55. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/agentEvalService.ts +30 -0
  56. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/agentTestService.ts +69 -0
  57. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/bookService.ts +21 -0
  58. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/createManGoAgentSource.ts +107 -0
  59. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/draftService.ts +45 -0
  60. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/services/uploadService.ts +27 -0
  61. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/state/OnboardingProvider.tsx +121 -0
  62. package/apps/agents-server/src/components/NewAgentDialog/ManGoNewAgentWizard/types.ts +52 -0
  63. package/apps/agents-server/src/components/NewAgentDialog/createNewAgentWizardSource.ts +1 -1
  64. package/apps/agents-server/src/components/NewAgentDialog/trackNewAgentCreationEvent.ts +1 -1
  65. package/apps/agents-server/src/components/NewAgentDialog/useNewAgentDialog.tsx +76 -0
  66. package/apps/agents-server/src/constants/newAgentWizard.ts +17 -8
  67. package/apps/agents-server/src/database/getMetadata.ts +37 -1
  68. package/apps/agents-server/src/database/metadataDefaults.ts +18 -7
  69. package/apps/agents-server/src/database/migrations/2026-06-2200-new-agent-wizard-metadata-key.sql +27 -0
  70. package/apps/agents-server/src/database/migrations/2026-06-2201-new-agent-wizard-mango-default.sql +11 -0
  71. package/apps/agents-server/src/generated/reservedPaths.ts +2 -0
  72. package/apps/agents-server/src/languages/translations/english.yaml +1 -1
  73. package/apps/agents-server/src/tools/createChatAttachmentToolFunctions.ts +8 -4
  74. package/apps/agents-server/src/utils/chat/resolveAgentsServerCitationLabel.ts +130 -0
  75. package/apps/agents-server/src/utils/chat/resolveCitationSourceLabel.ts +436 -0
  76. package/apps/agents-server/src/utils/knowledge/resolveWebsiteKnowledgeSourcesForServer.ts +10 -1
  77. package/apps/agents-server/src/utils/session.ts +62 -5
  78. package/apps/agents-server/src/utils/transpilers/resolveTranspiledTeamExport.ts +10 -10
  79. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +198 -63
  80. package/apps/agents-server/src/utils/vpsConfiguration.ts +1 -0
  81. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +81 -8
  82. package/esm/index.es.js +1052 -455
  83. package/esm/index.es.js.map +1 -1
  84. package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -1
  85. package/esm/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -2
  86. package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +50 -0
  87. package/esm/scripts/run-codex-prompts/server/buildCoderServerRunState.d.ts +23 -0
  88. package/esm/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  89. package/esm/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +13 -1
  90. package/esm/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  91. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -0
  92. package/esm/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -0
  93. package/esm/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +1 -0
  94. package/esm/src/_packages/components.index.d.ts +2 -0
  95. package/esm/src/_packages/types.index.d.ts +2 -0
  96. package/esm/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  97. package/esm/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  98. package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  99. package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  100. package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  101. package/esm/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  102. package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  103. package/esm/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  104. package/esm/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  105. package/esm/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  106. package/esm/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  107. package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  108. package/esm/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  109. package/esm/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  110. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  111. package/esm/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  112. package/esm/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  113. package/esm/src/version.d.ts +1 -1
  114. package/package.json +1 -1
  115. package/src/_packages/components.index.ts +2 -0
  116. package/src/_packages/types.index.ts +2 -0
  117. package/src/avatars/avatarAnimationScheduler.ts +61 -20
  118. package/src/avatars/visuals/octopus3d2AvatarVisual.ts +106 -21
  119. package/src/avatars/visuals/octopus3d3AvatarVisual.ts +131 -28
  120. package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.ts +18 -2
  121. package/src/book-2.0/book-language-documentation/renderGroupedCommitmentDocumentationMarkdown.ts +7 -1
  122. package/src/book-components/Chat/Chat/Chat.tsx +4 -0
  123. package/src/book-components/Chat/Chat/ChatCitationModal.tsx +17 -3
  124. package/src/book-components/Chat/Chat/ChatMessageItem.tsx +22 -2
  125. package/src/book-components/Chat/Chat/ChatMessageList.tsx +6 -0
  126. package/src/book-components/Chat/Chat/ChatProps.tsx +6 -0
  127. package/src/book-components/Chat/Chat/ChatToolCallModal.tsx +4 -0
  128. package/src/book-components/Chat/Chat/ChatToolCallModalContent.tsx +4 -0
  129. package/src/book-components/Chat/Chat/TeamToolCallModalContent.tsx +16 -3
  130. package/src/book-components/Chat/Chat/useChatPostprocessedMessages.ts +13 -2
  131. package/src/book-components/Chat/SourceChip/SourceChip.module.css +8 -0
  132. package/src/book-components/Chat/SourceChip/SourceChip.tsx +19 -5
  133. package/src/book-components/Chat/hooks/useResolvedCitationLabel.ts +65 -0
  134. package/src/book-components/Chat/save/html/htmlSaveFormatDefinition.ts +315 -88
  135. package/src/book-components/Chat/save/react/reactSaveFormatDefinition.ts +7 -1
  136. package/src/book-components/Chat/types/ChatMessage.ts +5 -0
  137. package/src/book-components/Chat/types/ChatParticipant.ts +1 -1
  138. package/src/book-components/Chat/types/CitationLabelResolver.ts +9 -0
  139. package/src/book-components/Chat/utils/citationHelpers.ts +90 -2
  140. package/src/book-components/Chat/utils/createCitationFootnoteRenderModel.ts +3 -0
  141. package/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.ts +178 -0
  142. package/src/book-components/Chat/utils/parseCitationsFromContent.ts +6 -0
  143. package/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.ts +13 -2
  144. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +1 -1
  145. package/src/cli/cli-commands/coder/run.ts +17 -20
  146. package/src/cli/cli-commands/coder/server.ts +13 -16
  147. package/src/cli/common/$deprecateCliCommand.ts +10 -1
  148. package/src/commitments/GOAL/GOAL.ts +7 -2
  149. package/src/commitments/LANGUAGE/LANGUAGE.ts +7 -2
  150. package/src/commitments/TEAM/TEAM.ts +12 -1
  151. package/src/commitments/USE_PROJECT/createUseProjectToolFunctions.ts +8 -1
  152. package/src/import-plugins/JsonFileImportPlugin.ts +15 -2
  153. package/src/import-plugins/TextFileImportPlugin.ts +8 -1
  154. package/src/llm-providers/_common/register/$registeredLlmToolsMessage.ts +6 -1
  155. package/src/other/templates/getTemplatesPipelineCollection.ts +681 -837
  156. package/src/utils/chat/chatAttachments/resolveChatAttachmentContent.ts +8 -1
  157. package/src/version.ts +2 -2
  158. package/src/versions.txt +1 -0
  159. package/umd/index.umd.js +1055 -458
  160. package/umd/index.umd.js.map +1 -1
  161. package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -1
  162. package/umd/scripts/run-codex-prompts/main/runPromptRound.d.ts +2 -2
  163. package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +50 -0
  164. package/umd/scripts/run-codex-prompts/server/buildCoderServerRunState.d.ts +23 -0
  165. package/umd/scripts/run-codex-prompts/server/coderServerHtml.d.ts +1 -1
  166. package/umd/scripts/run-codex-prompts/server/runCoderHttpServer.d.ts +13 -1
  167. package/umd/scripts/run-codex-prompts/server/updatePromptSection.d.ts +1 -1
  168. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -0
  169. package/umd/scripts/run-codex-prompts/ui/buildRunUiFrameShared.d.ts +4 -0
  170. package/umd/scripts/run-codex-prompts/ui/renderCoderRunUi.d.ts +1 -0
  171. package/umd/src/_packages/components.index.d.ts +2 -0
  172. package/umd/src/_packages/types.index.d.ts +2 -0
  173. package/umd/src/avatars/avatarAnimationScheduler.d.ts +4 -0
  174. package/umd/src/book-components/Chat/Chat/ChatCitationModal.d.ts +2 -0
  175. package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +4 -0
  176. package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +4 -0
  177. package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  178. package/umd/src/book-components/Chat/Chat/ChatToolCallModal.d.ts +2 -0
  179. package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +3 -1
  180. package/umd/src/book-components/Chat/Chat/TeamToolCallModalContent.d.ts +2 -0
  181. package/umd/src/book-components/Chat/SourceChip/SourceChip.d.ts +6 -1
  182. package/umd/src/book-components/Chat/hooks/useResolvedCitationLabel.d.ts +12 -0
  183. package/umd/src/book-components/Chat/types/ChatMessage.d.ts +4 -0
  184. package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +1 -1
  185. package/umd/src/book-components/Chat/types/CitationLabelResolver.d.ts +8 -0
  186. package/umd/src/book-components/Chat/utils/citationHelpers.d.ts +9 -0
  187. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.d.ts +14 -0
  188. package/umd/src/book-components/Chat/utils/decodeJsonUnicodeEscapesInMarkdownText.test.d.ts +1 -0
  189. package/umd/src/book-components/Chat/utils/parseCitationsFromContent.d.ts +4 -0
  190. package/umd/src/version.d.ts +1 -1
@@ -0,0 +1,114 @@
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+
3
+ import { ONBOARDING_FLOW, getFlowIndexByPath } from '../config/steps';
4
+ import { useManGoOnboardingNavigation } from '../ManGoOnboardingNavigation';
5
+ import { useOnboarding } from '../state/OnboardingProvider';
6
+ import { Logo } from './Logo';
7
+ import { RailStepper } from './RailStepper';
8
+ import { RailArtwork } from './brand/RailArtwork';
9
+
10
+ const RAIL_GRADIENT: CSSProperties = {
11
+ backgroundImage: 'var(--ob-grad-ink)',
12
+ };
13
+
14
+ /**
15
+ * Split-screen wizard chrome: a persistent accent rail (brand identity, agent name,
16
+ * vertical stepper, progress) beside the active step. The rail collapses to a compact
17
+ * top bar on small screens.
18
+ */
19
+ export function WizardShell({ children }: { readonly children: ReactNode }) {
20
+ const { currentPath, requestClose } = useManGoOnboardingNavigation();
21
+ const { state } = useOnboarding();
22
+
23
+ const activeIndex = getFlowIndexByPath(currentPath);
24
+ const total = ONBOARDING_FLOW.length;
25
+ const progress = Math.round(((activeIndex + 1) / total) * 100);
26
+ const agentName = state.agentName.trim() || 'Váš nový agent';
27
+
28
+ return (
29
+ <div data-onboarding-ui className="flex min-h-screen flex-col bg-zinc-50 text-zinc-900 lg:flex-row">
30
+ {/* Mobile: compact top bar */}
31
+ <div className="relative overflow-hidden text-white lg:hidden" style={RAIL_GRADIENT}>
32
+ <div className="relative z-10 flex items-center justify-between px-5 py-3.5">
33
+ <button
34
+ type="button"
35
+ onClick={requestClose}
36
+ className="rounded-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50"
37
+ >
38
+ <Logo className="text-white" />
39
+ </button>
40
+ <span className="max-w-[40%] truncate text-[13px] text-white/80">{agentName}</span>
41
+ <button
42
+ type="button"
43
+ onClick={requestClose}
44
+ aria-label="Zavřít průvodce"
45
+ className="flex h-8 w-8 items-center justify-center rounded-lg text-xl leading-none text-white/70 transition-colors hover:bg-white/10 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50"
46
+ >
47
+ ×
48
+ </button>
49
+ </div>
50
+ <div className="relative z-10 overflow-x-auto px-5 pb-3.5">
51
+ <RailStepper activeIndex={activeIndex} orientation="horizontal" />
52
+ </div>
53
+ </div>
54
+
55
+ {/* Desktop: persistent side rail */}
56
+ <aside
57
+ className="relative hidden w-80 flex-shrink-0 overflow-hidden p-7 text-white lg:sticky lg:top-0 lg:flex lg:h-screen lg:flex-col"
58
+ style={RAIL_GRADIENT}
59
+ >
60
+ <RailArtwork />
61
+ <div className="relative z-10 flex h-full flex-col">
62
+ <div className="flex items-center justify-between gap-3">
63
+ <button
64
+ type="button"
65
+ onClick={requestClose}
66
+ className="inline-flex w-fit rounded-lg transition-opacity hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50"
67
+ >
68
+ <Logo className="text-white" />
69
+ </button>
70
+ <button
71
+ type="button"
72
+ onClick={requestClose}
73
+ aria-label="Zavřít průvodce"
74
+ className="flex h-9 w-9 items-center justify-center rounded-lg text-2xl leading-none text-white/55 transition-colors hover:bg-white/10 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/50"
75
+ >
76
+ ×
77
+ </button>
78
+ </div>
79
+
80
+ <div className="mt-10">
81
+ <div className="text-xs font-medium text-white/60">Tvorba agenta</div>
82
+ <div className="ob-display mt-1 text-[19px] font-semibold leading-snug">{agentName}</div>
83
+ </div>
84
+
85
+ <div className="mt-9 flex-1">
86
+ <RailStepper activeIndex={activeIndex} orientation="vertical" />
87
+ </div>
88
+
89
+ <div className="relative z-10 mt-6">
90
+ <div className="mb-1.5 flex justify-between text-xs text-white/75">
91
+ <span>Průběh</span>
92
+ <span>
93
+ {activeIndex + 1} z {total}
94
+ </span>
95
+ </div>
96
+ <div className="h-1.5 overflow-hidden rounded-full bg-white/20">
97
+ <div
98
+ className="h-full rounded-full bg-[color:var(--ob-brand-blue)] transition-[width] duration-500 ease-out"
99
+ style={{ width: `${progress}%` }}
100
+ />
101
+ </div>
102
+ <p className="ob-display mt-5 text-[12px] leading-relaxed text-white/45">
103
+ Vytvořte AI, která skutečně rozumí vaší firmě.
104
+ </p>
105
+ </div>
106
+ </div>
107
+ </aside>
108
+
109
+ <main className="flex-1 px-5 py-8 sm:px-8 sm:py-12">
110
+ <div className="ob-animate-rise">{children}</div>
111
+ </main>
112
+ </div>
113
+ );
114
+ }
@@ -0,0 +1,60 @@
1
+ import { cn } from '../../lib/cn';
2
+
3
+ /**
4
+ * Ambient identity artwork woven into the accent rail — a large, faint "book → spark"
5
+ * motif (the same story as the logomark) that bleeds off the panel edges rather than
6
+ * sitting in a box. Purely decorative; white line art over the indigo gradient.
7
+ */
8
+ export function RailArtwork({ className }: { readonly className?: string }) {
9
+ return (
10
+ <svg
11
+ viewBox="0 0 320 480"
12
+ preserveAspectRatio="xMidYMax slice"
13
+ className={cn('pointer-events-none absolute inset-0 h-full w-full', className)}
14
+ fill="none"
15
+ aria-hidden="true"
16
+ >
17
+ <g stroke="#7aebff">
18
+ {/* faint concentric rings around the spark */}
19
+ <g strokeOpacity="0.14" strokeWidth="1">
20
+ <circle cx="232" cy="150" r="34" />
21
+ <circle cx="232" cy="150" r="58" />
22
+ <circle cx="232" cy="150" r="86" />
23
+ </g>
24
+
25
+ {/* large open book, bleeding off the bottom */}
26
+ <g strokeOpacity="0.28" strokeWidth="1.6" strokeLinejoin="round" strokeLinecap="round">
27
+ <path d="M150 360 L40 388 L40 500 L150 478 Z" />
28
+ <path d="M150 360 L260 388 L260 500 L150 478 Z" />
29
+ <line x1="150" y1="360" x2="150" y2="478" />
30
+ <g strokeOpacity="0.18" strokeWidth="1.3">
31
+ <line x1="60" y1="402" x2="128" y2="416" />
32
+ <line x1="60" y1="420" x2="128" y2="434" />
33
+ <line x1="172" y1="416" x2="240" y2="402" />
34
+ <line x1="172" y1="434" x2="240" y2="420" />
35
+ </g>
36
+ </g>
37
+
38
+ {/* flow rising from the spine to the spark */}
39
+ <g strokeOpacity="0.24" strokeWidth="1.5" strokeLinecap="round">
40
+ <path d="M150 356 C 178 286, 196 232, 232 158" />
41
+ <path d="M150 352 C 200 300, 214 226, 230 160" />
42
+ </g>
43
+
44
+ {/* spark */}
45
+ <g strokeOpacity="0.6" strokeWidth="1.6" strokeLinecap="round">
46
+ <line x1="232" y1="132" x2="232" y2="144" />
47
+ <line x1="232" y1="156" x2="232" y2="168" />
48
+ <line x1="214" y1="150" x2="226" y2="150" />
49
+ <line x1="238" y1="150" x2="250" y2="150" />
50
+ </g>
51
+ </g>
52
+ <circle cx="232" cy="150" r="5.5" fill="#7aebff" fillOpacity="0.7" />
53
+ <g fill="#7aebff" fillOpacity="0.35">
54
+ <circle cx="196" cy="206" r="2.5" />
55
+ <circle cx="262" cy="104" r="2" />
56
+ <circle cx="150" cy="318" r="2" />
57
+ </g>
58
+ </svg>
59
+ );
60
+ }
@@ -0,0 +1,135 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+
5
+ import { ONBOARDING_ENTRY_PATH, ONBOARDING_STEPS } from '../../config/steps';
6
+ import { useManGoOnboardingNavigation } from '../../ManGoOnboardingNavigation';
7
+ import { generateBookDraft } from '../../services/draftService';
8
+ import { useOnboarding } from '../../state/OnboardingProvider';
9
+ import { BookLanguagePanel } from '../BookLanguagePanel';
10
+ import { MarkdownBookEditor } from '../MarkdownBookEditor';
11
+ import { StepFooter, StepHeader } from '../StepFrame';
12
+ import { Banner } from '../ui/Banner';
13
+ import { Button } from '../ui/Button';
14
+ import { Card } from '../ui/Card';
15
+ import { Spinner } from '../ui/Spinner';
16
+
17
+ type Phase = 'init' | 'generating' | 'ready' | 'error';
18
+
19
+ export function BookStep() {
20
+ const { navigateToPath } = useManGoOnboardingNavigation();
21
+ const { state, isHydrated, update } = useOnboarding();
22
+ const [phase, setPhase] = useState<Phase>('init');
23
+ const [error, setError] = useState<string | null>(null);
24
+ const startedRef = useRef(false);
25
+
26
+ const isBriefAvailable = state.agentName.trim().length > 0 && state.agentBrief.trim().length > 0;
27
+
28
+ const generate = useCallback(async () => {
29
+ setPhase('generating');
30
+ setError(null);
31
+ try {
32
+ const draft = await generateBookDraft({ agentName: state.agentName, agentBrief: state.agentBrief });
33
+ update({ bookSource: draft });
34
+ setPhase('ready');
35
+ } catch (caught) {
36
+ setError(caught instanceof Error ? caught.message : 'Generování draftu selhalo.');
37
+ setPhase('error');
38
+ }
39
+ }, [state.agentName, state.agentBrief, update]);
40
+
41
+ // Generate the draft once, after hydration completes — so we read the persisted
42
+ // name/brief rather than the initial empty state. Runs a single time (startedRef),
43
+ // reading the latest values from the closure captured when hydration flips true.
44
+ useEffect(() => {
45
+ if (!isHydrated || startedRef.current) {
46
+ return;
47
+ }
48
+ startedRef.current = true;
49
+
50
+ if (state.bookSource.trim().length > 0) {
51
+ setPhase('ready');
52
+ } else if (isBriefAvailable) {
53
+ void generate();
54
+ } else {
55
+ setPhase('ready');
56
+ }
57
+ }, [generate, isBriefAvailable, isHydrated, state.bookSource]);
58
+
59
+ if (phase === 'init' || phase === 'generating') {
60
+ return (
61
+ <div className="mx-auto max-w-2xl">
62
+ <StepHeader eyebrow="Definice agenta" title="Připravujeme draft booku" />
63
+ <Card variant="elevated" className="flex flex-col items-center justify-center gap-5 px-8 py-20 text-center">
64
+ <span className="relative flex h-14 w-14 items-center justify-center">
65
+ <span className="absolute inset-0 animate-ping rounded-full bg-[color:var(--ob-accent-200)] opacity-60" />
66
+ <span className="relative flex h-14 w-14 items-center justify-center rounded-full bg-[color:var(--ob-accent-50)]">
67
+ <Spinner className="h-7 w-7" />
68
+ </span>
69
+ </span>
70
+ <div>
71
+ <p className="text-sm font-semibold text-zinc-800">Čtu vaše zadání a píšu draft book.md…</p>
72
+ <p className="mt-1 text-xs text-zinc-400">Chvíli to může trvat — generuje skutečný model.</p>
73
+ </div>
74
+ </Card>
75
+ </div>
76
+ );
77
+ }
78
+
79
+ const isContinueEnabled = state.bookSource.trim().length > 0;
80
+
81
+ return (
82
+ <div className="mx-auto max-w-4xl">
83
+ <StepHeader
84
+ eyebrow="Definice agenta"
85
+ title="Draft booku je hotový"
86
+ subtitle="Je to jen výchozí návrh — cokoli přepište, smažte nebo doplňte, klidně přímo na schůzce s klientem."
87
+ />
88
+
89
+ {phase === 'error' && (
90
+ <Banner tone="warning" title="Draft se nepodařilo vygenerovat." className="mb-5">
91
+ <p>{error}</p>
92
+ <p className="mt-1">Book můžete napsat i ručně níže, nebo to zkuste znovu.</p>
93
+ </Banner>
94
+ )}
95
+
96
+ {!isBriefAvailable && state.bookSource.trim().length === 0 && phase !== 'error' && (
97
+ <Banner tone="info" className="mb-5">
98
+ Nejprve vyplňte zadání, ať můžeme draft vygenerovat — nebo book napište ručně níže.{' '}
99
+ <button
100
+ type="button"
101
+ className="font-semibold text-[color:var(--ob-accent-700)] underline underline-offset-2 hover:text-[color:var(--ob-accent-800)]"
102
+ onClick={() => navigateToPath(ONBOARDING_ENTRY_PATH)}
103
+ >
104
+ Zpět na zadání
105
+ </button>
106
+ </Banner>
107
+ )}
108
+
109
+ <MarkdownBookEditor
110
+ value={state.bookSource}
111
+ onChange={(value) => update({ bookSource: value })}
112
+ onRegenerate={isBriefAvailable ? () => void generate() : undefined}
113
+ />
114
+
115
+ {state.bookSource.trim().length > 0 && <BookLanguagePanel source={state.bookSource} />}
116
+
117
+ <StepFooter
118
+ left={
119
+ <Button variant="ghost" onClick={() => navigateToPath(ONBOARDING_ENTRY_PATH)}>
120
+ ← Zadání
121
+ </Button>
122
+ }
123
+ right={
124
+ <Button
125
+ disabled={!isContinueEnabled}
126
+ trailingIcon={<span aria-hidden>→</span>}
127
+ onClick={() => navigateToPath(ONBOARDING_STEPS[1].path)}
128
+ >
129
+ Pokračovat: Znalosti
130
+ </Button>
131
+ }
132
+ />
133
+ </div>
134
+ );
135
+ }
@@ -0,0 +1,215 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import { useEffect, useRef } from 'react';
5
+ import { useState } from 'react';
6
+
7
+ import type { AgentVisibility } from '../../../../../utils/agentVisibility';
8
+ import { ONBOARDING_ENTRY_PATH, ONBOARDING_STEPS } from '../../config/steps';
9
+ import { useManGoOnboardingNavigation } from '../../ManGoOnboardingNavigation';
10
+ import { createManGoAgentSource } from '../../services/createManGoAgentSource';
11
+ import { useOnboarding } from '../../state/OnboardingProvider';
12
+ import { SectionLabel, StepCard } from '../StepFrame';
13
+ import { Banner } from '../ui/Banner';
14
+ import { Badge } from '../ui/Badge';
15
+ import { Button } from '../ui/Button';
16
+
17
+ /**
18
+ * Created-agent payload returned by the host Agents Server dialog controller.
19
+ */
20
+ export type ManGoCreatedAgentPayload = {
21
+ /**
22
+ * Permanent id of the created agent.
23
+ */
24
+ readonly permanentId: string;
25
+
26
+ /**
27
+ * Route opened when the user leaves the completion screen.
28
+ */
29
+ readonly targetPath: string;
30
+ };
31
+
32
+ /**
33
+ * Props accepted by the completion step.
34
+ */
35
+ type DoneStepProps = {
36
+ /**
37
+ * Default visibility resolved from server metadata.
38
+ */
39
+ readonly defaultVisibility: AgentVisibility;
40
+
41
+ /**
42
+ * Creates the real Agents Server agent.
43
+ */
44
+ readonly onCreate: (request: {
45
+ readonly agentSource: ReturnType<typeof createManGoAgentSource>;
46
+ readonly visibility: AgentVisibility;
47
+ readonly knowledgeCount: number;
48
+ }) => Promise<ManGoCreatedAgentPayload>;
49
+
50
+ /**
51
+ * Opens the created agent route selected by the host dialog controller.
52
+ */
53
+ readonly onOpenCreatedAgent: (targetPath: string) => void;
54
+ };
55
+
56
+ function SummaryRow({ label, value }: { readonly label: string; readonly value: ReactNode }) {
57
+ return (
58
+ <div className="flex items-center justify-between gap-4 border-b border-zinc-100 py-2.5 last:border-b-0">
59
+ <span className="text-sm text-zinc-500">{label}</span>
60
+ <span className="text-sm font-medium text-zinc-900">{value}</span>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export function DoneStep(props: DoneStepProps) {
66
+ const { defaultVisibility, onCreate, onOpenCreatedAgent } = props;
67
+ const { navigateToPath } = useManGoOnboardingNavigation();
68
+ const { state, isHydrated, update, reset } = useOnboarding();
69
+ const savedRef = useRef(false);
70
+ const [isCreatingAgent, setIsCreatingAgent] = useState(false);
71
+ const [creationError, setCreationError] = useState<string | null>(null);
72
+ const [creationAttempt, setCreationAttempt] = useState(0);
73
+
74
+ const knowledgeCount = state.knowledge.length;
75
+ const isTested = state.testMessages.some((message) => message.role === 'user');
76
+ const isBookAvailable = state.bookSource.trim().length > 0;
77
+ const isCreated = Boolean(state.savedAgentId && state.savedAgentTargetPath);
78
+
79
+ // Create the real agent once. Guarded by both a ref (single run) and the session's
80
+ // saved permanent id (don't re-create on revisit).
81
+ useEffect(() => {
82
+ if (!isHydrated || savedRef.current) {
83
+ return;
84
+ }
85
+ savedRef.current = true;
86
+
87
+ if (state.agentName.trim().length === 0 && state.bookSource.trim().length === 0) {
88
+ return;
89
+ }
90
+
91
+ if (state.savedAgentId && state.savedAgentTargetPath) {
92
+ return;
93
+ }
94
+
95
+ const agentSource = createManGoAgentSource(state);
96
+ const readyKnowledgeCount = state.knowledge.filter((item) => item.status === 'ready').length;
97
+
98
+ setIsCreatingAgent(true);
99
+ setCreationError(null);
100
+ void onCreate({
101
+ agentSource,
102
+ visibility: defaultVisibility,
103
+ knowledgeCount: readyKnowledgeCount,
104
+ })
105
+ .then((agent) => {
106
+ update({
107
+ savedAgentId: agent.permanentId,
108
+ savedAgentTargetPath: agent.targetPath,
109
+ });
110
+ })
111
+ .catch((error) => {
112
+ savedRef.current = false;
113
+ setCreationError(error instanceof Error ? error.message : 'Vytvoření agenta selhalo.');
114
+ })
115
+ .finally(() => {
116
+ setIsCreatingAgent(false);
117
+ });
118
+ }, [creationAttempt, defaultVisibility, isHydrated, onCreate, state, update]);
119
+
120
+ function startOver() {
121
+ reset();
122
+ navigateToPath(ONBOARDING_ENTRY_PATH);
123
+ }
124
+
125
+ return (
126
+ <div className="mx-auto max-w-xl text-center">
127
+ <div className="relative mx-auto mb-6 flex h-20 w-20 items-center justify-center">
128
+ <span className="absolute inset-0 rounded-full bg-emerald-100/70 blur-md" aria-hidden />
129
+ <span className="relative flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br from-emerald-50 to-emerald-100 text-4xl shadow-[var(--ob-shadow-md)] ring-1 ring-emerald-200/60">
130
+ 🎉
131
+ </span>
132
+ </div>
133
+ <h1 className="ob-display text-[28px] font-bold tracking-tight text-zinc-900">Agent v1 je uložen!</h1>
134
+ <p className="mt-2 text-sm text-zinc-500">
135
+ {state.agentName.trim()
136
+ ? `${state.agentName.trim()} je připraven k dalšímu rozvoji.`
137
+ : 'Agent je připraven k dalšímu rozvoji.'}
138
+ </p>
139
+
140
+ {isCreatingAgent && (
141
+ <Banner tone="info" className="mt-6 text-left" title="Ukládám agenta">
142
+ První verze se ukládá do Agents Serveru.
143
+ </Banner>
144
+ )}
145
+
146
+ {creationError && (
147
+ <Banner tone="error" className="mt-6 text-left" title="Agenta se nepodařilo uložit">
148
+ {creationError}
149
+ </Banner>
150
+ )}
151
+
152
+ <StepCard className="mt-8 text-left">
153
+ <SectionLabel className="mb-3">Shrnutí první verze</SectionLabel>
154
+ <SummaryRow label="Název agenta" value={state.agentName.trim() || '—'} />
155
+ <SummaryRow
156
+ label="Book"
157
+ value={
158
+ <Badge tone={isBookAvailable ? 'success' : 'neutral'} dot={isBookAvailable}>
159
+ {isBookAvailable ? 'Definován' : 'Prázdný'}
160
+ </Badge>
161
+ }
162
+ />
163
+ <SummaryRow
164
+ label="Znalostní báze"
165
+ value={
166
+ <Badge tone={knowledgeCount > 0 ? 'accent' : 'neutral'}>
167
+ {knowledgeCount === 0 ? 'Žádné zdroje' : `${knowledgeCount} zdroje/ů`}
168
+ </Badge>
169
+ }
170
+ />
171
+ <SummaryRow
172
+ label="Testování"
173
+ value={
174
+ <Badge tone={isTested ? 'success' : 'neutral'} dot={isTested}>
175
+ {isTested ? 'Provedeno' : 'Neproběhlo'}
176
+ </Badge>
177
+ }
178
+ />
179
+ <SummaryRow
180
+ label="Uložení"
181
+ value={
182
+ <Badge tone={isCreated ? 'success' : creationError ? 'error' : 'neutral'} dot={isCreated}>
183
+ {isCreated ? 'Uloženo' : creationError ? 'Chyba' : 'Probíhá'}
184
+ </Badge>
185
+ }
186
+ />
187
+ </StepCard>
188
+
189
+ <div className="mt-7 flex flex-col gap-3">
190
+ <Button
191
+ trailingIcon={<span aria-hidden>→</span>}
192
+ disabled={(!state.savedAgentTargetPath && !creationError) || isCreatingAgent}
193
+ onClick={() => {
194
+ if (creationError) {
195
+ setCreationAttempt((previousAttempt) => previousAttempt + 1);
196
+ return;
197
+ }
198
+
199
+ if (state.savedAgentTargetPath) {
200
+ onOpenCreatedAgent(state.savedAgentTargetPath);
201
+ }
202
+ }}
203
+ >
204
+ {creationError ? 'Zkusit uložit znovu' : 'Přejít na chat agenta'}
205
+ </Button>
206
+ <Button variant="outline" disabled={isCreatingAgent} onClick={() => navigateToPath(ONBOARDING_STEPS[0].path)}>
207
+ Upravit agenta
208
+ </Button>
209
+ <Button variant="ghost" disabled={isCreatingAgent} onClick={startOver}>
210
+ Začít nový onboarding
211
+ </Button>
212
+ </div>
213
+ </div>
214
+ );
215
+ }
@@ -0,0 +1,152 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+
5
+ import { ONBOARDING_STEPS } from '../../config/steps';
6
+ import { createId } from '../../lib/id';
7
+ import { uploadKnowledgeFile } from '../../services/uploadService';
8
+ import { useManGoOnboardingNavigation } from '../../ManGoOnboardingNavigation';
9
+ import { useOnboarding } from '../../state/OnboardingProvider';
10
+ import type { KnowledgeFileItem } from '../../types';
11
+ import { cn } from '../../lib/cn';
12
+ import { DropZone } from '../DropZone';
13
+ import { KnowledgeList } from '../KnowledgeList';
14
+ import { StepCard, StepFooter, StepHeader } from '../StepFrame';
15
+ import { Button } from '../ui/Button';
16
+ import { CONTROL, CONTROL_ERROR } from '../ui/Field';
17
+
18
+ const MAX_FILE_SIZE_BYTES = 25 * 1024 * 1024;
19
+
20
+ function normalizeUrl(raw: string): string | null {
21
+ const trimmed = raw.trim();
22
+ if (!trimmed) {
23
+ return null;
24
+ }
25
+ const withProtocol = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
26
+ try {
27
+ return new URL(withProtocol).href;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ export function KnowledgeStep() {
34
+ const { navigateToPath } = useManGoOnboardingNavigation();
35
+ const { state, update } = useOnboarding();
36
+ const [urlInput, setUrlInput] = useState('');
37
+ const [urlError, setUrlError] = useState<string | null>(null);
38
+
39
+ function uploadOne(file: File) {
40
+ const id = createId();
41
+ const item: KnowledgeFileItem = {
42
+ kind: 'file',
43
+ id,
44
+ name: file.name,
45
+ size: file.size,
46
+ publicUrl: '',
47
+ objectKey: '',
48
+ status: 'uploading',
49
+ };
50
+ update((prev) => ({ knowledge: [...prev.knowledge, item] }));
51
+
52
+ uploadKnowledgeFile(file)
53
+ .then(({ publicUrl, objectKey }) => {
54
+ update((prev) => ({
55
+ knowledge: prev.knowledge.map((entry) =>
56
+ entry.id === id ? { ...entry, publicUrl, objectKey, status: 'ready' } : entry,
57
+ ),
58
+ }));
59
+ })
60
+ .catch(() => {
61
+ update((prev) => ({
62
+ knowledge: prev.knowledge.map((entry) =>
63
+ entry.id === id ? { ...entry, status: 'error' } : entry,
64
+ ),
65
+ }));
66
+ });
67
+ }
68
+
69
+ function handleFiles(files: readonly File[]) {
70
+ files.filter((file) => file.size <= MAX_FILE_SIZE_BYTES).forEach(uploadOne);
71
+ }
72
+
73
+ function handleAddUrl() {
74
+ const normalized = normalizeUrl(urlInput);
75
+ if (!normalized) {
76
+ setUrlError('Zadejte platnou adresu, např. https://firma.cz/napoveda');
77
+ return;
78
+ }
79
+ setUrlError(null);
80
+ setUrlInput('');
81
+ update((prev) => ({
82
+ knowledge: [...prev.knowledge, { kind: 'url', id: createId(), url: normalized, status: 'ready' }],
83
+ }));
84
+ }
85
+
86
+ function handleRemove(id: string) {
87
+ update((prev) => ({ knowledge: prev.knowledge.filter((entry) => entry.id !== id) }));
88
+ }
89
+
90
+ return (
91
+ <div className="mx-auto max-w-2xl">
92
+ <StepHeader
93
+ eyebrow="Znalostní báze"
94
+ title="Přidejte znalosti agenta"
95
+ subtitle="Agent čerpá odpovědi ze souborů a odkazů, které mu dáte. Čím relevantnější materiály, tím přesnější výstupy."
96
+ />
97
+
98
+ <StepCard className="space-y-6">
99
+ <DropZone onFiles={handleFiles} hint="PDF, DOCX, TXT, XLSX · max. 25 MB na soubor" />
100
+
101
+ <KnowledgeList items={state.knowledge} onRemove={handleRemove} />
102
+
103
+ <div className="border-t border-zinc-100 pt-6">
104
+ <label htmlFor="knowledge-url" className="mb-1.5 block text-[13px] font-semibold text-zinc-700">
105
+ Nebo přidejte odkaz na web / dokumentaci
106
+ </label>
107
+ <div className="flex gap-2.5">
108
+ <input
109
+ id="knowledge-url"
110
+ type="text"
111
+ value={urlInput}
112
+ placeholder="https://firma.cz/napoveda"
113
+ aria-invalid={urlError ? true : undefined}
114
+ onChange={(event) => setUrlInput(event.target.value)}
115
+ onKeyDown={(event) => event.key === 'Enter' && handleAddUrl()}
116
+ className={cn(CONTROL, urlError && CONTROL_ERROR)}
117
+ />
118
+ <Button
119
+ variant="outline"
120
+ onClick={handleAddUrl}
121
+ className="whitespace-nowrap"
122
+ leadingIcon={<span aria-hidden>+</span>}
123
+ >
124
+ Přidat
125
+ </Button>
126
+ </div>
127
+ {urlError ? (
128
+ <p className="mt-1 text-xs text-red-600">{urlError}</p>
129
+ ) : (
130
+ <p className="mt-1 text-xs text-zinc-400">Agent si stránku přečte a zahrne do znalostní báze.</p>
131
+ )}
132
+ </div>
133
+ </StepCard>
134
+
135
+ <StepFooter
136
+ left={
137
+ <Button variant="ghost" onClick={() => navigateToPath(ONBOARDING_STEPS[0].path)}>
138
+ ← Book
139
+ </Button>
140
+ }
141
+ right={
142
+ <Button
143
+ trailingIcon={<span aria-hidden>→</span>}
144
+ onClick={() => navigateToPath(ONBOARDING_STEPS[2].path)}
145
+ >
146
+ Pokračovat: Test
147
+ </Button>
148
+ }
149
+ />
150
+ </div>
151
+ );
152
+ }