@promptbook/cli 0.112.0-132 → 0.112.0-134

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 (101) hide show
  1. package/apps/agents-server/src/app/admin/api-tokens/ApiTokensClient.tsx +4 -1
  2. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +3 -0
  3. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackTable.tsx +13 -8
  4. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +3 -0
  5. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryTable.tsx +13 -8
  6. package/apps/agents-server/src/app/admin/custom-css/CustomCssClient.tsx +4 -0
  7. package/apps/agents-server/src/app/admin/custom-css/CustomCssEditorPanel.tsx +7 -1
  8. package/apps/agents-server/src/app/admin/custom-css/CustomCssFilesPanel.tsx +7 -1
  9. package/apps/agents-server/src/app/admin/custom-js/CustomJsClient.tsx +4 -0
  10. package/apps/agents-server/src/app/admin/custom-js/CustomJsEditorPanel.tsx +7 -1
  11. package/apps/agents-server/src/app/admin/custom-js/CustomJsFilesPanel.tsx +7 -1
  12. package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +4 -0
  13. package/apps/agents-server/src/app/admin/files/FilesGalleryGrid.tsx +9 -11
  14. package/apps/agents-server/src/app/admin/files/FilesGalleryTable.tsx +9 -8
  15. package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +4 -0
  16. package/apps/agents-server/src/app/admin/images/ImagesGalleryGrid.tsx +16 -11
  17. package/apps/agents-server/src/app/admin/images/ImagesGalleryTable.tsx +18 -9
  18. package/apps/agents-server/src/app/admin/login-methods/shibboleth/page.tsx +9 -9
  19. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +7 -6
  20. package/apps/agents-server/src/app/admin/servers/ServersRegistryTable.tsx +11 -10
  21. package/apps/agents-server/src/app/admin/task-manager/TaskManagerClient.tsx +4 -2
  22. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTaskRow.tsx +14 -15
  23. package/apps/agents-server/src/app/admin/task-manager/TaskManagerTasksCard.tsx +4 -1
  24. package/apps/agents-server/src/app/admin/task-manager/useTaskManagerState.ts +6 -9
  25. package/apps/agents-server/src/app/admin/update/CustomCommitPicker.tsx +258 -0
  26. package/apps/agents-server/src/app/admin/update/UpdateClient.tsx +305 -108
  27. package/apps/agents-server/src/app/admin/usage/UsageClient.tsx +4 -2
  28. package/apps/agents-server/src/app/admin/usage/UsageClientAnalyticsPanels.tsx +6 -4
  29. package/apps/agents-server/src/app/admin/usage/UsageClientFormatting.ts +6 -12
  30. package/apps/agents-server/src/app/admin/usage/UsageClientTimelineChart.tsx +6 -3
  31. package/apps/agents-server/src/app/admin/usage/useUsageClientState.ts +12 -4
  32. package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +8 -3
  33. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +6 -20
  34. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +2 -0
  35. package/apps/agents-server/src/app/agents/[agentName]/book/useBookEditorHistory.ts +9 -2
  36. package/apps/agents-server/src/app/agents/[agentName]/chat/useAgentChatHistoryClientState.ts +2 -15
  37. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +6 -1
  38. package/apps/agents-server/src/app/agents/[agentName]/integration/CalendarIntegrationSection.tsx +23 -5
  39. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsClient.tsx +4 -2
  40. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsFiltersCard.tsx +9 -4
  41. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableCard.tsx +4 -1
  42. package/apps/agents-server/src/app/agents/[agentName]/timeouts/AgentTimeoutsTableRow.tsx +10 -3
  43. package/apps/agents-server/src/app/api/admin/update/commits/route.ts +35 -0
  44. package/apps/agents-server/src/app/api/admin/update/route.ts +10 -2
  45. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +5 -0
  46. package/apps/agents-server/src/app/api/internal/user-chat-jobs/run/route.ts +14 -2
  47. package/apps/agents-server/src/app/api/v1/agents/[agentId]/route.ts +4 -0
  48. package/apps/agents-server/src/app/api/v1/agents/route.ts +2 -0
  49. package/apps/agents-server/src/app/dashboard/page.tsx +11 -16
  50. package/apps/agents-server/src/app/recycle-bin/actions.ts +3 -0
  51. package/apps/agents-server/src/app/swagger/SwaggerApiKeysPanel.tsx +4 -1
  52. package/apps/agents-server/src/app/system/user-memory/UserMemoryClient.tsx +4 -2
  53. package/apps/agents-server/src/app/system/user-wallet/UserWalletClient.tsx +3 -0
  54. package/apps/agents-server/src/app/system/user-wallet/UserWalletRecordsTable.tsx +5 -1
  55. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorClient.tsx +3 -0
  56. package/apps/agents-server/src/app/system/utilities/mocked-chats/MockedChatsEditorSidebar.tsx +7 -8
  57. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -2
  58. package/apps/agents-server/src/components/UsersList/UsersList.tsx +4 -2
  59. package/apps/agents-server/src/database/migrations/2026-06-2600-agent-directory-performance-indexes.sql +14 -0
  60. package/apps/agents-server/src/database/sqlite/$provideLocalSqliteSupabase.ts +92 -1
  61. package/apps/agents-server/src/tools/createAgentProgressTools.ts +8 -6
  62. package/apps/agents-server/src/utils/localization/formatServerLanguageHumanReadableDate.ts +70 -0
  63. package/apps/agents-server/src/utils/localization/getRequestServerLanguage.ts +32 -0
  64. package/apps/agents-server/src/utils/userChat/createRunUserChatJobExecutionContext.ts +17 -0
  65. package/apps/agents-server/src/utils/userChat/createRunUserChatJobPersistenceController.ts +0 -21
  66. package/apps/agents-server/src/utils/userChat/listUserChats.ts +5 -1
  67. package/apps/agents-server/src/utils/userChat/persistUserChatJobProgressCard.ts +31 -0
  68. package/apps/agents-server/src/utils/userChat/retryUserChatJob.ts +0 -4
  69. package/apps/agents-server/src/utils/userChat/runUserChatJob.ts +29 -0
  70. package/apps/agents-server/src/utils/userChat/userChatMessageLifecycle.ts +2 -2
  71. package/apps/agents-server/src/utils/userChat/userChatProgressCard.ts +99 -401
  72. package/apps/agents-server/src/utils/vpsSelfUpdate.ts +625 -51
  73. package/esm/index.es.js +252 -41
  74. package/esm/index.es.js.map +1 -1
  75. package/esm/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  76. package/esm/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  77. package/esm/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  78. package/esm/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  79. package/esm/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  80. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  81. package/esm/src/book-3.0/cliAgentEnv.d.ts +1 -1
  82. package/esm/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  83. package/esm/src/version.d.ts +1 -1
  84. package/package.json +1 -1
  85. package/src/book-3.0/cliAgentEnv.ts +1 -1
  86. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +1 -1
  87. package/src/cli/cli-commands/coder/ThinkingLevel.ts +3 -1
  88. package/src/other/templates/getTemplatesPipelineCollection.ts +911 -741
  89. package/src/version.ts +2 -2
  90. package/src/versions.txt +2 -0
  91. package/umd/index.umd.js +254 -44
  92. package/umd/index.umd.js.map +1 -1
  93. package/umd/scripts/run-codex-prompts/common/buildCoderRunProgressSnapshot.d.ts +5 -1
  94. package/umd/scripts/run-codex-prompts/common/cliProgressDisplay.d.ts +7 -0
  95. package/umd/scripts/run-codex-prompts/common/coderRunEstimateCache.d.ts +31 -0
  96. package/umd/scripts/run-codex-prompts/common/progressFormatting.d.ts +2 -0
  97. package/umd/scripts/run-codex-prompts/runners/claude-code/ClaudeCodeRunner.d.ts +3 -1
  98. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +7 -0
  99. package/umd/src/book-3.0/cliAgentEnv.d.ts +1 -1
  100. package/umd/src/cli/cli-commands/coder/ThinkingLevel.d.ts +1 -1
  101. package/umd/src/version.d.ts +1 -1
@@ -2,9 +2,11 @@
2
2
 
3
3
  import { Copy, Plus, Trash } from 'lucide-react';
4
4
  import { useEffect, useState } from 'react';
5
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
5
6
  import { SecretInput } from '../../../components/SecretInput/SecretInput';
6
7
  import { showConfirm } from '../../../components/AsyncDialogs/asyncDialogs';
7
8
  import { ApiTokenEntry, createApiToken, deleteApiToken, fetchApiTokens } from '../../../utils/apiTokensClient';
9
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
8
10
 
9
11
  /**
10
12
  * Props for rendering a single API token in a masked input.
@@ -44,6 +46,7 @@ function TokenSecretField({ token, onCopy }: TokenSecretFieldProps) {
44
46
  * Renders the admin API token management UI.
45
47
  */
46
48
  export function ApiTokensClient() {
49
+ const { language } = useServerLanguage();
47
50
  const [tokens, setTokens] = useState<ApiTokenEntry[]>([]);
48
51
  const [loading, setLoading] = useState(true);
49
52
  const [error, setError] = useState<string | null>(null);
@@ -182,7 +185,7 @@ export function ApiTokensClient() {
182
185
  </td>
183
186
  <td className="px-6 py-4 text-sm text-gray-500">{entry.note || '-'}</td>
184
187
  <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
185
- {new Date(entry.createdAt).toLocaleDateString()}
188
+ {formatServerLanguageHumanReadableDate(entry.createdAt, language)}
186
189
  </td>
187
190
  <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
188
191
  <button
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useAgentNaming } from '../../../components/AgentNaming/AgentNamingContext';
4
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
4
5
  import { ChatFeedbackFiltersCard } from './ChatFeedbackFiltersCard';
5
6
  import { ChatFeedbackTable } from './ChatFeedbackTable';
6
7
  import { ChatFeedbackThreadDialog } from './ChatFeedbackThreadDialog';
@@ -21,6 +22,7 @@ type ChatFeedbackClientProps = {
21
22
  */
22
23
  export function ChatFeedbackClient({ initialAgentName }: ChatFeedbackClientProps) {
23
24
  const { formatText } = useAgentNaming();
25
+ const { language } = useServerLanguage();
24
26
  const chatFeedbackState = useChatFeedbackState({ initialAgentName, formatText });
25
27
 
26
28
  return (
@@ -68,6 +70,7 @@ export function ChatFeedbackClient({ initialAgentName }: ChatFeedbackClientProps
68
70
 
69
71
  <ChatFeedbackTable
70
72
  formatText={formatText}
73
+ language={language}
71
74
  items={chatFeedbackState.items}
72
75
  total={chatFeedbackState.total}
73
76
  loading={chatFeedbackState.loading}
@@ -1,4 +1,6 @@
1
1
  import { Card } from '../../../components/Homepage/Card';
2
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
3
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
2
4
  import type { UseChatFeedbackState } from './useChatFeedbackState';
3
5
 
4
6
  /**
@@ -25,18 +27,18 @@ type ChatFeedbackTableProps = Pick<
25
27
  * Active text formatter for agent naming.
26
28
  */
27
29
  formatText: (text: string) => string;
30
+
31
+ /**
32
+ * Active UI language used for date formatting.
33
+ */
34
+ language: ServerLanguageCode;
28
35
  };
29
36
 
30
37
  /**
31
38
  * Formats date.
32
39
  */
33
- function formatDate(dateString: string | null | undefined): string {
34
- if (!dateString) return '-';
35
-
36
- const date = new Date(dateString);
37
- if (Number.isNaN(date.getTime())) return dateString;
38
-
39
- return date.toLocaleString();
40
+ function formatDate(dateString: string | null | undefined, language: ServerLanguageCode): string {
41
+ return formatServerLanguageHumanReadableDate(dateString, language, { fallbackLabel: '-' });
40
42
  }
41
43
 
42
44
  /**
@@ -71,6 +73,7 @@ function ChatFeedbackPreviewCell({ value }: { value: unknown }) {
71
73
  */
72
74
  export function ChatFeedbackTable({
73
75
  formatText,
76
+ language,
74
77
  items,
75
78
  total,
76
79
  loading,
@@ -136,7 +139,9 @@ export function ChatFeedbackTable({
136
139
  <tbody className="divide-y divide-gray-200 bg-white">
137
140
  {items.map((row) => (
138
141
  <tr key={row.id}>
139
- <td className="whitespace-nowrap px-4 py-3 text-gray-700">{formatDate(row.createdAt)}</td>
142
+ <td className="whitespace-nowrap px-4 py-3 text-gray-700">
143
+ {formatDate(row.createdAt, language)}
144
+ </td>
140
145
  <td className="whitespace-nowrap px-4 py-3 text-gray-700">{row.agentName}</td>
141
146
  <td className="whitespace-nowrap px-4 py-3 text-gray-700">{row.rating || '-'}</td>
142
147
  <td className="max-w-xs px-4 py-3 text-gray-700">
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { MockedChat } from '@promptbook-local/components';
4
4
  import { useAgentNaming } from '../../../components/AgentNaming/AgentNamingContext';
5
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
5
6
  import { ChatHistoryFiltersCard } from './ChatHistoryFiltersCard';
6
7
  import { ChatHistoryPagination } from './ChatHistoryPagination';
7
8
  import { ChatHistoryTable } from './ChatHistoryTable';
@@ -22,6 +23,7 @@ type ChatHistoryClientProps = {
22
23
  */
23
24
  export function ChatHistoryClient({ initialAgentName }: ChatHistoryClientProps) {
24
25
  const { formatText } = useAgentNaming();
26
+ const { language } = useServerLanguage();
25
27
  const chatHistoryState = useChatHistoryState({ initialAgentName, formatText });
26
28
 
27
29
  return (
@@ -116,6 +118,7 @@ export function ChatHistoryClient({ initialAgentName }: ChatHistoryClientProps)
116
118
  ) : (
117
119
  <ChatHistoryTable
118
120
  formatText={formatText}
121
+ language={language}
119
122
  items={chatHistoryState.items}
120
123
  total={chatHistoryState.total}
121
124
  loading={chatHistoryState.loading}
@@ -1,4 +1,6 @@
1
1
  import { Card } from '../../../components/Homepage/Card';
2
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
3
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
2
4
  import { ChatHistoryPagination } from './ChatHistoryPagination';
3
5
  import type { UseChatHistoryState } from './useChatHistoryState';
4
6
 
@@ -25,18 +27,18 @@ type ChatHistoryTableProps = Pick<
25
27
  * Active text formatter for agent naming.
26
28
  */
27
29
  formatText: (text: string) => string;
30
+
31
+ /**
32
+ * Active UI language used for date formatting.
33
+ */
34
+ language: ServerLanguageCode;
28
35
  };
29
36
 
30
37
  /**
31
38
  * Formats date.
32
39
  */
33
- function formatDate(dateString: string | null | undefined): string {
34
- if (!dateString) return '-';
35
-
36
- const date = new Date(dateString);
37
- if (Number.isNaN(date.getTime())) return dateString;
38
-
39
- return date.toLocaleString();
40
+ function formatDate(dateString: string | null | undefined, language: ServerLanguageCode): string {
41
+ return formatServerLanguageHumanReadableDate(dateString, language, { fallbackLabel: '-' });
40
42
  }
41
43
 
42
44
  /**
@@ -92,6 +94,7 @@ function ChatHistoryPreviewCell({ message }: { message: unknown }) {
92
94
  */
93
95
  export function ChatHistoryTable({
94
96
  formatText,
97
+ language,
95
98
  items,
96
99
  total,
97
100
  loading,
@@ -154,7 +157,9 @@ export function ChatHistoryTable({
154
157
  <tbody className="divide-y divide-gray-200 bg-white">
155
158
  {items.map((row) => (
156
159
  <tr key={row.id}>
157
- <td className="whitespace-nowrap px-4 py-3 text-gray-700">{formatDate(row.createdAt)}</td>
160
+ <td className="whitespace-nowrap px-4 py-3 text-gray-700">
161
+ {formatDate(row.createdAt, language)}
162
+ </td>
158
163
  <td className="whitespace-nowrap px-4 py-3 text-gray-700">{row.agentName}</td>
159
164
  <td className="whitespace-nowrap px-4 py-3 text-gray-700">{getMessageRole(row.message)}</td>
160
165
  <td className="max-w-xs px-4 py-3 text-gray-700">
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
3
4
  import { CustomCssEditorPanel } from './CustomCssEditorPanel';
4
5
  import { CustomCssFilesPanel } from './CustomCssFilesPanel';
5
6
  import { CustomCssSelectorsPanel } from './CustomCssSelectorsPanel';
@@ -11,6 +12,7 @@ import { useCustomCssClientState } from './useCustomCssClientState';
11
12
  * @private route component of CustomCssPage
12
13
  */
13
14
  export function CustomCssClient() {
15
+ const { language } = useServerLanguage();
14
16
  const {
15
17
  addNewFile,
16
18
  currentFile,
@@ -60,6 +62,7 @@ export function CustomCssClient() {
60
62
  <section className="rounded-lg border border-gray-200 bg-white p-4 shadow-sm">
61
63
  <div className="grid gap-4 lg:grid-cols-[260px_1fr]">
62
64
  <CustomCssFilesPanel
65
+ language={language}
63
66
  files={files}
64
67
  selectedFileLocalId={currentFile?.localId ?? ''}
65
68
  onAddNewFile={addNewFile}
@@ -67,6 +70,7 @@ export function CustomCssClient() {
67
70
  />
68
71
 
69
72
  <CustomCssEditorPanel
73
+ language={language}
70
74
  currentFile={currentFile}
71
75
  remainingCharacters={remainingCharacters}
72
76
  maxLength={maxLength}
@@ -3,6 +3,8 @@
3
3
  import type { ChangeEvent } from 'react';
4
4
  import { MonacoEditorWithShadowDom } from '../../../components/_utils/MonacoEditorWithShadowDom';
5
5
  import { usePromptbookTheme } from '../../../components/ThemeMode/usePromptbookTheme';
6
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
7
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
6
8
  import {
7
9
  CUSTOM_RESOURCE_INPUT_CLASS_NAME,
8
10
  CUSTOM_RESOURCE_PRIMARY_BUTTON_CLASS_NAME,
@@ -16,6 +18,7 @@ import type { CustomStylesheetFileState } from './CustomStylesheetFileState';
16
18
  * @private function of CustomCssClient
17
19
  */
18
20
  type CustomCssEditorPanelProps = {
21
+ readonly language: ServerLanguageCode;
19
22
  readonly currentFile: CustomStylesheetFileState | null;
20
23
  readonly remainingCharacters: number;
21
24
  readonly maxLength: number;
@@ -38,6 +41,7 @@ type CustomCssEditorPanelProps = {
38
41
  * @private function of CustomCssClient
39
42
  */
40
43
  export function CustomCssEditorPanel({
44
+ language,
41
45
  currentFile,
42
46
  remainingCharacters,
43
47
  maxLength,
@@ -60,7 +64,9 @@ export function CustomCssEditorPanel({
60
64
  <div className="mb-3 flex flex-wrap items-center justify-between gap-3">
61
65
  <h2 className="text-lg font-semibold text-gray-900 dark:text-slate-100">Stylesheet editor</h2>
62
66
  {currentFile?.updatedAt && (
63
- <div className="text-xs text-gray-500 dark:text-slate-400">Last saved: {new Date(currentFile.updatedAt).toLocaleString()}</div>
67
+ <div className="text-xs text-gray-500 dark:text-slate-400">
68
+ Last saved: {formatServerLanguageHumanReadableDate(currentFile.updatedAt, language)}
69
+ </div>
64
70
  )}
65
71
  </div>
66
72
 
@@ -1,3 +1,5 @@
1
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
2
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
1
3
  import type { CustomStylesheetFileState } from './CustomStylesheetFileState';
2
4
 
3
5
  /**
@@ -6,6 +8,7 @@ import type { CustomStylesheetFileState } from './CustomStylesheetFileState';
6
8
  * @private function of CustomCssClient
7
9
  */
8
10
  type CustomCssFilesPanelProps = {
11
+ readonly language: ServerLanguageCode;
9
12
  readonly files: ReadonlyArray<CustomStylesheetFileState>;
10
13
  readonly selectedFileLocalId: string;
11
14
  readonly onAddNewFile: () => void;
@@ -18,6 +21,7 @@ type CustomCssFilesPanelProps = {
18
21
  * @private function of CustomCssClient
19
22
  */
20
23
  export function CustomCssFilesPanel({
24
+ language,
21
25
  files,
22
26
  selectedFileLocalId,
23
27
  onAddNewFile,
@@ -59,7 +63,9 @@ export function CustomCssFilesPanel({
59
63
  >
60
64
  <div className="text-sm font-medium text-gray-900">{file.scope || 'Untitled stylesheet'}</div>
61
65
  <div className="text-xs text-gray-500">
62
- {file.updatedAt ? `Saved ${new Date(file.updatedAt).toLocaleString()}` : 'Not saved yet'}
66
+ {file.updatedAt
67
+ ? `Saved ${formatServerLanguageHumanReadableDate(file.updatedAt, language)}`
68
+ : 'Not saved yet'}
63
69
  </div>
64
70
  </button>
65
71
  );
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
3
4
  import { CustomJsAnalyticsPanel } from './CustomJsAnalyticsPanel';
4
5
  import { CustomJsEditorPanel } from './CustomJsEditorPanel';
5
6
  import { CustomJsFilesPanel } from './CustomJsFilesPanel';
@@ -11,6 +12,7 @@ import { useCustomJsClientState } from './useCustomJsClientState';
11
12
  * @private route component of CustomJsPage
12
13
  */
13
14
  export function CustomJsClient() {
15
+ const { language } = useServerLanguage();
14
16
  const {
15
17
  addNewFile,
16
18
  analyticsHasChanges,
@@ -74,6 +76,7 @@ export function CustomJsClient() {
74
76
 
75
77
  <div className="grid gap-4 lg:grid-cols-[260px_1fr]">
76
78
  <CustomJsFilesPanel
79
+ language={language}
77
80
  files={files}
78
81
  selectedFileLocalId={currentFile?.localId ?? ''}
79
82
  onAddNewFile={addNewFile}
@@ -81,6 +84,7 @@ export function CustomJsClient() {
81
84
  />
82
85
 
83
86
  <CustomJsEditorPanel
87
+ language={language}
84
88
  currentFile={currentFile}
85
89
  remainingCharacters={remainingCharacters}
86
90
  maxLength={maxLength}
@@ -3,6 +3,8 @@
3
3
  import type { ChangeEvent } from 'react';
4
4
  import { MonacoEditorWithShadowDom } from '../../../components/_utils/MonacoEditorWithShadowDom';
5
5
  import { usePromptbookTheme } from '../../../components/ThemeMode/usePromptbookTheme';
6
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
7
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
6
8
  import {
7
9
  CUSTOM_RESOURCE_INPUT_CLASS_NAME,
8
10
  CUSTOM_RESOURCE_PRIMARY_BUTTON_CLASS_NAME,
@@ -16,6 +18,7 @@ import type { CustomJavascriptFileState } from './CustomJavascriptFileState';
16
18
  * @private function of CustomJsClient
17
19
  */
18
20
  type CustomJsEditorPanelProps = {
21
+ readonly language: ServerLanguageCode;
19
22
  readonly currentFile: CustomJavascriptFileState | null;
20
23
  readonly remainingCharacters: number;
21
24
  readonly maxLength: number;
@@ -38,6 +41,7 @@ type CustomJsEditorPanelProps = {
38
41
  * @private function of CustomJsClient
39
42
  */
40
43
  export function CustomJsEditorPanel({
44
+ language,
41
45
  currentFile,
42
46
  remainingCharacters,
43
47
  maxLength,
@@ -60,7 +64,9 @@ export function CustomJsEditorPanel({
60
64
  <div className="mb-4 flex flex-wrap items-center justify-between gap-3">
61
65
  <h2 className="text-lg font-semibold text-gray-900 dark:text-slate-100">Script editor</h2>
62
66
  {currentFile?.updatedAt && (
63
- <div className="text-xs text-gray-500 dark:text-slate-400">Last saved: {new Date(currentFile.updatedAt).toLocaleString()}</div>
67
+ <div className="text-xs text-gray-500 dark:text-slate-400">
68
+ Last saved: {formatServerLanguageHumanReadableDate(currentFile.updatedAt, language)}
69
+ </div>
64
70
  )}
65
71
  </div>
66
72
 
@@ -1,3 +1,5 @@
1
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
2
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
1
3
  import type { CustomJavascriptFileState } from './CustomJavascriptFileState';
2
4
 
3
5
  /**
@@ -6,6 +8,7 @@ import type { CustomJavascriptFileState } from './CustomJavascriptFileState';
6
8
  * @private function of CustomJsClient
7
9
  */
8
10
  type CustomJsFilesPanelProps = {
11
+ readonly language: ServerLanguageCode;
9
12
  readonly files: ReadonlyArray<CustomJavascriptFileState>;
10
13
  readonly selectedFileLocalId: string;
11
14
  readonly onAddNewFile: () => void;
@@ -18,6 +21,7 @@ type CustomJsFilesPanelProps = {
18
21
  * @private function of CustomJsClient
19
22
  */
20
23
  export function CustomJsFilesPanel({
24
+ language,
21
25
  files,
22
26
  selectedFileLocalId,
23
27
  onAddNewFile,
@@ -59,7 +63,9 @@ export function CustomJsFilesPanel({
59
63
  >
60
64
  <div className="text-sm font-medium text-gray-900">{file.scope || 'Untitled script'}</div>
61
65
  <div className="text-xs text-gray-500">
62
- {file.updatedAt ? `Saved ${new Date(file.updatedAt).toLocaleString()}` : 'Not saved yet'}
66
+ {file.updatedAt
67
+ ? `Saved ${formatServerLanguageHumanReadableDate(file.updatedAt, language)}`
68
+ : 'Not saved yet'}
63
69
  </div>
64
70
  </button>
65
71
  );
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useAgentNaming } from '../../../components/AgentNaming/AgentNamingContext';
4
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
4
5
  import { FilesGalleryGrid } from './FilesGalleryGrid';
5
6
  import { FilesGalleryTable } from './FilesGalleryTable';
6
7
  import { FilesGalleryViewModeToggle } from './FilesGalleryViewModeToggle';
@@ -11,6 +12,7 @@ import { useFilesGalleryState } from './useFilesGalleryState';
11
12
  */
12
13
  export function FilesGalleryClient() {
13
14
  const { formatText } = useAgentNaming();
15
+ const { language } = useServerLanguage();
14
16
  const filesGalleryState = useFilesGalleryState();
15
17
 
16
18
  return (
@@ -26,6 +28,7 @@ export function FilesGalleryClient() {
26
28
  {filesGalleryState.viewMode === 'TABLE' ? (
27
29
  <FilesGalleryTable
28
30
  formatText={formatText}
31
+ language={language}
29
32
  files={filesGalleryState.files}
30
33
  total={filesGalleryState.total}
31
34
  isLoading={filesGalleryState.isLoading}
@@ -36,6 +39,7 @@ export function FilesGalleryClient() {
36
39
  ) : (
37
40
  <FilesGalleryGrid
38
41
  formatText={formatText}
42
+ language={language}
39
43
  files={filesGalleryState.files}
40
44
  isLoading={filesGalleryState.isLoading}
41
45
  hasMore={filesGalleryState.hasMore}
@@ -1,6 +1,8 @@
1
1
  import { File, Loader2 } from 'lucide-react';
2
2
  import Link from 'next/link';
3
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
3
4
  import { buildAgentProfileHref } from '../../../utils/agentRouting/agentRouteHrefs';
5
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
4
6
  import { FilesGalleryStatusBadge } from './FilesGalleryStatusBadge';
5
7
  import type { UseFilesGalleryState } from './useFilesGalleryState';
6
8
 
@@ -12,6 +14,11 @@ type FilesGalleryGridProps = Pick<UseFilesGalleryState, 'files' | 'isLoading' |
12
14
  * Active text formatter for agent naming.
13
15
  */
14
16
  readonly formatText: (text: string) => string;
17
+
18
+ /**
19
+ * Active UI language used for date formatting.
20
+ */
21
+ readonly language: ServerLanguageCode;
15
22
  };
16
23
 
17
24
  /**
@@ -21,16 +28,6 @@ function formatFilesGalleryCompactFileSize(fileSize: number): string {
21
28
  return `${(fileSize / 1024).toFixed(1)} KB`;
22
29
  }
23
30
 
24
- /**
25
- * Formats one created-at value for the compact grid view.
26
- */
27
- function formatFilesGalleryCompactCreatedAt(createdAt: string): string {
28
- return new Date(createdAt).toLocaleDateString(undefined, {
29
- month: 'short',
30
- day: 'numeric',
31
- });
32
- }
33
-
34
31
  /**
35
32
  * Renders the grid view of the files gallery, including infinite-scroll feedback.
36
33
  *
@@ -38,6 +35,7 @@ function formatFilesGalleryCompactCreatedAt(createdAt: string): string {
38
35
  */
39
36
  export function FilesGalleryGrid({
40
37
  formatText,
38
+ language,
41
39
  files,
42
40
  isLoading,
43
41
  hasMore,
@@ -82,7 +80,7 @@ export function FilesGalleryGrid({
82
80
  <span className="text-xs text-gray-400">{formatText('No agent')}</span>
83
81
  )}
84
82
  <span className="text-[10px] text-gray-400 whitespace-nowrap">
85
- {formatFilesGalleryCompactCreatedAt(file.createdAt)}
83
+ {formatServerLanguageHumanReadableDate(file.createdAt, language)}
86
84
  </span>
87
85
  </div>
88
86
  <p className="text-xs text-gray-600 truncate" title={file.fileName}>
@@ -1,6 +1,8 @@
1
1
  import { ChevronLeft, ChevronRight } from 'lucide-react';
2
2
  import Link from 'next/link';
3
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
3
4
  import { buildAgentProfileHref } from '../../../utils/agentRouting/agentRouteHrefs';
5
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
4
6
  import { FilesGalleryStatusBadge } from './FilesGalleryStatusBadge';
5
7
  import type { UseFilesGalleryState } from './useFilesGalleryState';
6
8
 
@@ -15,6 +17,11 @@ type FilesGalleryTableProps = Pick<
15
17
  * Active text formatter for agent naming.
16
18
  */
17
19
  readonly formatText: (text: string) => string;
20
+
21
+ /**
22
+ * Active UI language used for date formatting.
23
+ */
24
+ readonly language: ServerLanguageCode;
18
25
  };
19
26
 
20
27
  /**
@@ -24,13 +31,6 @@ function formatFilesGalleryFileSize(fileSize: number): string {
24
31
  return `${(fileSize / 1024).toFixed(2)} KB`;
25
32
  }
26
33
 
27
- /**
28
- * Formats one created-at value for the table view.
29
- */
30
- function formatFilesGalleryCreatedAt(createdAt: string): string {
31
- return new Date(createdAt).toLocaleString();
32
- }
33
-
34
34
  /**
35
35
  * Renders the table view of the files gallery, including pagination.
36
36
  *
@@ -38,6 +38,7 @@ function formatFilesGalleryCreatedAt(createdAt: string): string {
38
38
  */
39
39
  export function FilesGalleryTable({
40
40
  formatText,
41
+ language,
41
42
  files,
42
43
  total,
43
44
  isLoading,
@@ -105,7 +106,7 @@ export function FilesGalleryTable({
105
106
  <FilesGalleryStatusBadge status={file.status} />
106
107
  </td>
107
108
  <td className="px-6 py-4 whitespace-nowrap">
108
- {formatFilesGalleryCreatedAt(file.createdAt)}
109
+ {formatServerLanguageHumanReadableDate(file.createdAt, language)}
109
110
  </td>
110
111
  </tr>
111
112
  ))}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useAgentNaming } from '../../../components/AgentNaming/AgentNamingContext';
4
+ import { useServerLanguage } from '../../../components/ServerLanguage/ServerLanguageProvider';
4
5
  import { ImagesGalleryGrid } from './ImagesGalleryGrid';
5
6
  import { ImagesGalleryTable } from './ImagesGalleryTable';
6
7
  import { ImagesGalleryViewModeToggle } from './ImagesGalleryViewModeToggle';
@@ -11,6 +12,7 @@ import { useImagesGalleryState } from './useImagesGalleryState';
11
12
  */
12
13
  export function ImagesGalleryClient() {
13
14
  const { formatText } = useAgentNaming();
15
+ const { language } = useServerLanguage();
14
16
  const imagesGalleryState = useImagesGalleryState();
15
17
 
16
18
  return (
@@ -26,6 +28,7 @@ export function ImagesGalleryClient() {
26
28
  {imagesGalleryState.viewMode === 'TABLE' ? (
27
29
  <ImagesGalleryTable
28
30
  formatText={formatText}
31
+ language={language}
29
32
  images={imagesGalleryState.images}
30
33
  total={imagesGalleryState.total}
31
34
  isLoading={imagesGalleryState.isLoading}
@@ -38,6 +41,7 @@ export function ImagesGalleryClient() {
38
41
  ) : (
39
42
  <ImagesGalleryGrid
40
43
  formatText={formatText}
44
+ language={language}
41
45
  images={imagesGalleryState.images}
42
46
  isLoading={imagesGalleryState.isLoading}
43
47
  hasMore={imagesGalleryState.hasMore}
@@ -1,6 +1,8 @@
1
1
  import { Loader2 } from 'lucide-react';
2
2
  import Link from 'next/link';
3
+ import type { ServerLanguageCode } from '../../../languages/ServerLanguageRegistry';
3
4
  import { buildAgentProfileHref } from '../../../utils/agentRouting/agentRouteHrefs';
5
+ import { formatServerLanguageHumanReadableDate } from '../../../utils/localization/formatServerLanguageHumanReadableDate';
4
6
  import type { ImageWithAgent } from './actions';
5
7
  import { ImagesGalleryParametersBadges } from './ImagesGalleryParametersBadges';
6
8
  import { ImagesGalleryPromptCopyButton } from './ImagesGalleryPromptCopyButton';
@@ -18,6 +20,11 @@ type ImagesGalleryGridProps = Pick<
18
20
  * Active text formatter for agent naming.
19
21
  */
20
22
  readonly formatText: (text: string) => string;
23
+
24
+ /**
25
+ * Active UI language used for date formatting.
26
+ */
27
+ readonly language: ServerLanguageCode;
21
28
  };
22
29
 
23
30
  /**
@@ -33,17 +40,12 @@ type ImagesGalleryGridCardProps = Pick<UseImagesGalleryState, 'copiedId' | 'hand
33
40
  * Active text formatter for agent naming.
34
41
  */
35
42
  readonly formatText: (text: string) => string;
36
- };
37
43
 
38
- /**
39
- * Formats one created-at value for the compact grid view.
40
- */
41
- function formatImagesGalleryCompactCreatedAt(createdAt: string): string {
42
- return new Date(createdAt).toLocaleDateString(undefined, {
43
- month: 'short',
44
- day: 'numeric',
45
- });
46
- }
44
+ /**
45
+ * Active UI language used for date formatting.
46
+ */
47
+ readonly language: ServerLanguageCode;
48
+ };
47
49
 
48
50
  /**
49
51
  * Renders one image card inside the gallery grid.
@@ -51,6 +53,7 @@ function formatImagesGalleryCompactCreatedAt(createdAt: string): string {
51
53
  function ImagesGalleryGridCard({
52
54
  image,
53
55
  formatText,
56
+ language,
54
57
  copiedId,
55
58
  handlePromptCopy,
56
59
  }: ImagesGalleryGridCardProps) {
@@ -91,7 +94,7 @@ function ImagesGalleryGridCard({
91
94
  <span className="text-xs text-gray-400">{formatText('No agent')}</span>
92
95
  )}
93
96
  <span className="text-[10px] text-gray-400 whitespace-nowrap">
94
- {formatImagesGalleryCompactCreatedAt(image.createdAt)}
97
+ {formatServerLanguageHumanReadableDate(image.createdAt, language)}
95
98
  </span>
96
99
  </div>
97
100
  <div className="relative group/prompt">
@@ -119,6 +122,7 @@ function ImagesGalleryGridCard({
119
122
  */
120
123
  export function ImagesGalleryGrid({
121
124
  formatText,
125
+ language,
122
126
  images,
123
127
  isLoading,
124
128
  hasMore,
@@ -134,6 +138,7 @@ export function ImagesGalleryGrid({
134
138
  key={image.id}
135
139
  image={image}
136
140
  formatText={formatText}
141
+ language={language}
137
142
  copiedId={copiedId}
138
143
  handlePromptCopy={handlePromptCopy}
139
144
  />