@utopia-studio-design/design-system-cli 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/bin/utopia-ds-mcp.mjs +9 -2
- package/bin/utopia-ds.mjs +106 -8
- package/data/blocks/activity-feed/ActivityFeedBlock.tsx +255 -0
- package/data/blocks/activity-feed/README.md +16 -0
- package/data/blocks/activity-feed/activity-feed-contract.ts +96 -0
- package/data/blocks/activity-feed/activity-feed-fixture.ts +22 -0
- package/data/blocks/activity-feed/activity-feed.css +154 -0
- package/data/blocks/agent-status/AgentStatusBlock.tsx +313 -0
- package/data/blocks/agent-status/README.md +15 -0
- package/data/blocks/agent-status/agent-status-contract.ts +133 -0
- package/data/blocks/agent-status/agent-status-fixture.ts +79 -0
- package/data/blocks/agent-status/agent-status.css +216 -0
- package/data/blocks/analytics-dashboard/AnalyticsDashboardBlocks.tsx +107 -0
- package/data/blocks/analytics-dashboard/AnalyticsMetricBlocks.tsx +87 -0
- package/data/blocks/analytics-dashboard/README.md +17 -0
- package/data/blocks/analytics-dashboard/analytics-dashboard.css +92 -0
- package/data/blocks/calendar-application/CalendarApplicationBlocks.tsx +519 -0
- package/data/blocks/calendar-application/README.md +20 -0
- package/data/blocks/calendar-application/calendar-application.css +216 -0
- package/data/blocks/calendar-application/calendar-localization.ts +90 -0
- package/data/blocks/calendar-application/calendar-panel-contract.ts +72 -0
- package/data/blocks/dashboard-composer/CeramicPuckAdapter.tsx +380 -0
- package/data/blocks/dashboard-composer/DashboardComposerBlocks.tsx +337 -0
- package/data/blocks/dashboard-composer/README.md +16 -0
- package/data/blocks/dashboard-composer/RegistryDataBlocks.tsx +238 -0
- package/data/blocks/dashboard-composer/dashboard-composer.css +328 -0
- package/data/blocks/database-workspace/DatabaseWorkspaceBlocks.tsx +240 -0
- package/data/blocks/database-workspace/README.md +9 -0
- package/data/blocks/database-workspace/database-workspace.css +107 -0
- package/data/blocks/interaction-workbench/InteractionWorkbench.tsx +271 -0
- package/data/blocks/interaction-workbench/README.md +9 -0
- package/data/blocks/interaction-workbench/interaction-workbench.css +226 -0
- package/data/docs/calendar.md +164 -0
- package/data/docs/charts.md +95 -0
- package/data/docs/composition-registry.md +174 -0
- package/data/docs/dashboard-composer.md +94 -0
- package/data/docs/database.md +68 -0
- package/data/docs/examples/dashboard-layout.v1.json +65 -0
- package/data/docs/foundations.md +1 -1
- package/data/docs/interaction-foundation.md +109 -0
- package/data/docs/quick-start-ai.md +23 -1
- package/data/manifests/blocks.json +392 -0
- package/data/manifests/catalog.json +7 -4
- package/data/manifests/components.json +179 -0
- package/data/manifests/composition-registry.json +427 -0
- package/data/manifests/motion-profiles.json +1 -1
- package/data/manifests/patterns.json +113 -1
- package/data/manifests/templates.json +346 -0
- package/data/manifests/theme-renacore.json +142 -0
- package/data/manifests/themes.json +314 -28
- package/data/templates/analytics-dashboard/AnalyticsDashboardBlocks.tsx +107 -0
- package/data/templates/analytics-dashboard/AnalyticsMetricBlocks.tsx +87 -0
- package/data/templates/analytics-dashboard/README.md +6 -0
- package/data/templates/analytics-dashboard/analytics-dashboard.css +92 -0
- package/data/templates/analytics-dashboard/index.html +13 -0
- package/data/templates/analytics-dashboard/main.tsx +196 -0
- package/data/templates/analytics-dashboard/styles.css +120 -0
- package/data/templates/analytics-dashboard/template.manifest.json +17 -0
- package/data/templates/calendar-application/CalendarApplicationBlocks.tsx +519 -0
- package/data/templates/calendar-application/README.md +10 -0
- package/data/templates/calendar-application/calendar-application.css +216 -0
- package/data/templates/calendar-application/calendar-localization.ts +90 -0
- package/data/templates/calendar-application/index.html +14 -0
- package/data/templates/calendar-application/main.tsx +409 -0
- package/data/templates/calendar-application/styles.css +248 -0
- package/data/templates/calendar-application/template.manifest.json +27 -0
- package/data/templates/composition-registry-lab/README.md +9 -0
- package/data/templates/composition-registry-lab/index.html +14 -0
- package/data/templates/composition-registry-lab/main.tsx +217 -0
- package/data/templates/composition-registry-lab/sample-layout.json +63 -0
- package/data/templates/composition-registry-lab/styles.css +343 -0
- package/data/templates/composition-registry-lab/template.manifest.json +34 -0
- package/data/templates/dashboard-composer/ActivityFeedBlock.tsx +255 -0
- package/data/templates/dashboard-composer/AgentStatusBlock.tsx +313 -0
- package/data/templates/dashboard-composer/AnalyticsDashboardBlocks.tsx +107 -0
- package/data/templates/dashboard-composer/AnalyticsMetricBlocks.tsx +87 -0
- package/data/templates/dashboard-composer/CalendarApplicationBlocks.tsx +519 -0
- package/data/templates/dashboard-composer/CeramicPuckAdapter.tsx +380 -0
- package/data/templates/dashboard-composer/DashboardComposerBlocks.tsx +337 -0
- package/data/templates/dashboard-composer/DatabaseWorkspaceBlocks.tsx +240 -0
- package/data/templates/dashboard-composer/README.md +53 -0
- package/data/templates/dashboard-composer/RegistryDataBlocks.tsx +238 -0
- package/data/templates/dashboard-composer/activity-feed-contract.ts +96 -0
- package/data/templates/dashboard-composer/activity-feed-fixture.ts +22 -0
- package/data/templates/dashboard-composer/activity-feed.css +154 -0
- package/data/templates/dashboard-composer/agent-status-contract.ts +133 -0
- package/data/templates/dashboard-composer/agent-status-fixture.ts +79 -0
- package/data/templates/dashboard-composer/agent-status-lab/index.html +19 -0
- package/data/templates/dashboard-composer/agent-status-lab/main.tsx +151 -0
- package/data/templates/dashboard-composer/agent-status-lab/styles.css +139 -0
- package/data/templates/dashboard-composer/agent-status.css +216 -0
- package/data/templates/dashboard-composer/analytics-dashboard.css +92 -0
- package/data/templates/dashboard-composer/calendar-application.css +216 -0
- package/data/templates/dashboard-composer/calendar-localization.ts +90 -0
- package/data/templates/dashboard-composer/calendar-panel-contract.ts +72 -0
- package/data/templates/dashboard-composer/dashboard-composer.css +328 -0
- package/data/templates/dashboard-composer/database-workspace.css +107 -0
- package/data/templates/dashboard-composer/index.html +14 -0
- package/data/templates/dashboard-composer/main.tsx +550 -0
- package/data/templates/dashboard-composer/sample-layout.json +147 -0
- package/data/templates/dashboard-composer/styles.css +198 -0
- package/data/templates/dashboard-composer/template.manifest.json +34 -0
- package/data/templates/dashboard-composer/visual-editor/PuckEditorSurface.tsx +132 -0
- package/data/templates/dashboard-composer/visual-editor/VisualEditorApp.tsx +343 -0
- package/data/templates/dashboard-composer/visual-editor/index.html +17 -0
- package/data/templates/dashboard-composer/visual-editor/main.tsx +95 -0
- package/data/templates/dashboard-composer/visual-editor/styles.css +310 -0
- package/data/templates/database-workspace/DatabaseWorkspaceBlocks.tsx +240 -0
- package/data/templates/database-workspace/README.md +5 -0
- package/data/templates/database-workspace/database-workspace.css +107 -0
- package/data/templates/database-workspace/index.html +14 -0
- package/data/templates/database-workspace/main.tsx +247 -0
- package/data/templates/database-workspace/styles.css +121 -0
- package/data/templates/database-workspace/template.manifest.json +19 -0
- package/data/templates/interaction-lab/InteractionWorkbench.tsx +271 -0
- package/data/templates/interaction-lab/README.md +7 -0
- package/data/templates/interaction-lab/index.html +14 -0
- package/data/templates/interaction-lab/interaction-workbench.css +226 -0
- package/data/templates/interaction-lab/main.tsx +104 -0
- package/data/templates/interaction-lab/styles.css +224 -0
- package/data/templates/interaction-lab/template.manifest.json +36 -0
- package/lib/api.mjs +72 -5
- package/lib/composition-runtime.mjs +382 -0
- package/package.json +4 -1
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { Button } from '@utopia-studio-design/design-system/Button'
|
|
3
|
+
import type {
|
|
4
|
+
ChartAnnotationDefinition,
|
|
5
|
+
ChartDatum,
|
|
6
|
+
ChartSeriesDefinition,
|
|
7
|
+
ChartType,
|
|
8
|
+
} from '@utopia-studio-design/design-system/Charts'
|
|
9
|
+
import type {
|
|
10
|
+
DatabaseQuery,
|
|
11
|
+
DatabaseRecord,
|
|
12
|
+
DatabaseSchema,
|
|
13
|
+
} from '@utopia-studio-design/design-system/Database'
|
|
14
|
+
import type { CeramicLayoutBlock } from '@utopia-studio-design/design-system/Composition'
|
|
15
|
+
import { DatabaseTableBlock } from './DatabaseWorkspaceBlocks'
|
|
16
|
+
|
|
17
|
+
const LazyAnalyticsChartCard = React.lazy(async () => {
|
|
18
|
+
const module = await import('./AnalyticsDashboardBlocks')
|
|
19
|
+
return { default: module.AnalyticsChartCard }
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const issueSchema: DatabaseSchema = {
|
|
23
|
+
id: 'composer-issues',
|
|
24
|
+
title: 'Delivery issues',
|
|
25
|
+
primaryPropertyId: 'title',
|
|
26
|
+
properties: [
|
|
27
|
+
{ id: 'title', label: 'Issue', type: 'text', required: true, width: 260 },
|
|
28
|
+
{
|
|
29
|
+
id: 'status',
|
|
30
|
+
label: 'Status',
|
|
31
|
+
type: 'select',
|
|
32
|
+
width: 150,
|
|
33
|
+
options: [
|
|
34
|
+
{ id: 'active', label: 'Active', tone: 'warning' },
|
|
35
|
+
{ id: 'backlog', label: 'Backlog' },
|
|
36
|
+
{ id: 'blocked', label: 'Blocked', tone: 'negative' },
|
|
37
|
+
{ id: 'completed', label: 'Completed', tone: 'positive' },
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'priority',
|
|
42
|
+
label: 'Priority',
|
|
43
|
+
type: 'select',
|
|
44
|
+
width: 130,
|
|
45
|
+
options: [
|
|
46
|
+
{ id: 'high', label: 'High', tone: 'negative' },
|
|
47
|
+
{ id: 'medium', label: 'Medium', tone: 'warning' },
|
|
48
|
+
{ id: 'low', label: 'Low' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{ id: 'owner', label: 'Owner', type: 'person', width: 140 },
|
|
52
|
+
{ id: 'due', label: 'Due', type: 'date', width: 150 },
|
|
53
|
+
],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const issueRecords: DatabaseRecord[] = [
|
|
57
|
+
{ id: 'issue-1', values: { title: 'Audit Arabic calendar navigation', status: 'active', priority: 'high', owner: 'Mina', due: '2026-08-01' } },
|
|
58
|
+
{ id: 'issue-2', values: { title: 'Review visual-editor payload', status: 'active', priority: 'medium', owner: 'Aiden', due: '2026-08-03' } },
|
|
59
|
+
{ id: 'issue-3', values: { title: 'Document registry permissions', status: 'backlog', priority: 'medium', owner: 'Haajar', due: '2026-08-05' } },
|
|
60
|
+
{ id: 'issue-4', values: { title: 'Resolve mobile table overflow', status: 'blocked', priority: 'high', owner: 'Sora', due: '2026-07-31' } },
|
|
61
|
+
{ id: 'issue-5', values: { title: 'Verify Button asChild regression', status: 'completed', priority: 'high', owner: 'Mina', due: '2026-07-29' } },
|
|
62
|
+
{ id: 'issue-6', values: { title: 'Publish Calendar QA fixture', status: 'completed', priority: 'low', owner: 'Aiden', due: '2026-07-30' } },
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
const chartData: ChartDatum[] = [
|
|
66
|
+
{ id: 'mon', label: 'Mon', values: { completed: 18, previous: 14 } },
|
|
67
|
+
{ id: 'tue', label: 'Tue', values: { completed: 23, previous: 17 } },
|
|
68
|
+
{ id: 'wed', label: 'Wed', values: { completed: 20, previous: 19 } },
|
|
69
|
+
{ id: 'thu', label: 'Thu', values: { completed: 28, previous: 21 } },
|
|
70
|
+
{ id: 'fri', label: 'Fri', values: { completed: 31, previous: 25 } },
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
const currentSeries: ChartSeriesDefinition = {
|
|
74
|
+
id: 'completed',
|
|
75
|
+
label: 'Completed work',
|
|
76
|
+
tone: 'primary',
|
|
77
|
+
}
|
|
78
|
+
const previousSeries: ChartSeriesDefinition = {
|
|
79
|
+
id: 'previous',
|
|
80
|
+
label: 'Previous period',
|
|
81
|
+
pattern: 'dash',
|
|
82
|
+
tone: 'benchmark',
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function issueQuery(view: unknown): DatabaseQuery {
|
|
86
|
+
if (view === 'active') {
|
|
87
|
+
return {
|
|
88
|
+
filters: [{
|
|
89
|
+
id: 'active-issues',
|
|
90
|
+
propertyId: 'status',
|
|
91
|
+
operator: 'not-equals',
|
|
92
|
+
value: 'completed',
|
|
93
|
+
}],
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (view === 'backlog' || view === 'blocked' || view === 'completed') {
|
|
97
|
+
return {
|
|
98
|
+
filters: [{
|
|
99
|
+
id: `${view}-issues`,
|
|
100
|
+
propertyId: 'status',
|
|
101
|
+
operator: 'equals',
|
|
102
|
+
value: view,
|
|
103
|
+
}],
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function IssueTableRegistryBlock({
|
|
110
|
+
block,
|
|
111
|
+
direction,
|
|
112
|
+
}: {
|
|
113
|
+
block: CeramicLayoutBlock
|
|
114
|
+
direction: 'ltr' | 'rtl'
|
|
115
|
+
}) {
|
|
116
|
+
const requestedColumns = Array.isArray(block.props.columns)
|
|
117
|
+
? block.props.columns.filter((column): column is string => typeof column === 'string')
|
|
118
|
+
: []
|
|
119
|
+
const visibleColumnIds = new Set(
|
|
120
|
+
requestedColumns.length
|
|
121
|
+
? ['title', ...requestedColumns]
|
|
122
|
+
: issueSchema.properties.map((property) => property.id),
|
|
123
|
+
)
|
|
124
|
+
const density = (
|
|
125
|
+
block.props.density === 'compact'
|
|
126
|
+
|| block.props.density === 'comfortable'
|
|
127
|
+
|| block.props.density === 'default'
|
|
128
|
+
)
|
|
129
|
+
? block.props.density
|
|
130
|
+
: 'default'
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div className="ceramic-issue-table-block">
|
|
134
|
+
<DatabaseTableBlock
|
|
135
|
+
density={density}
|
|
136
|
+
direction={direction}
|
|
137
|
+
permissions={{
|
|
138
|
+
bulkActions: false,
|
|
139
|
+
edit: false,
|
|
140
|
+
reorderColumns: false,
|
|
141
|
+
reorderRows: false,
|
|
142
|
+
resizeColumns: false,
|
|
143
|
+
}}
|
|
144
|
+
query={issueQuery(block.props.view)}
|
|
145
|
+
records={issueRecords}
|
|
146
|
+
schema={{
|
|
147
|
+
...issueSchema,
|
|
148
|
+
properties: issueSchema.properties.map((property) => ({
|
|
149
|
+
...property,
|
|
150
|
+
hidden: !visibleColumnIds.has(property.id),
|
|
151
|
+
})),
|
|
152
|
+
}}
|
|
153
|
+
state="ready"
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class ChartLoadBoundary extends React.Component<
|
|
160
|
+
{ children: React.ReactNode },
|
|
161
|
+
{ failed: boolean }
|
|
162
|
+
> {
|
|
163
|
+
state = { failed: false }
|
|
164
|
+
|
|
165
|
+
static getDerivedStateFromError() {
|
|
166
|
+
return { failed: true }
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
componentDidCatch(error: Error) {
|
|
170
|
+
console.error('The registered chart renderer failed to load.', error)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
render() {
|
|
174
|
+
if (this.state.failed) {
|
|
175
|
+
return (
|
|
176
|
+
<div className="ceramic-chart-panel-state" role="alert">
|
|
177
|
+
<strong>Chart preview could not load.</strong>
|
|
178
|
+
<span>The layout remains valid. Retry the optional visualization module.</span>
|
|
179
|
+
<Button size="sm" variant="outline" onClick={() => window.location.reload()}>
|
|
180
|
+
Retry chart
|
|
181
|
+
</Button>
|
|
182
|
+
</div>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
return this.props.children
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export function ChartPanelRegistryBlock({
|
|
190
|
+
block,
|
|
191
|
+
direction,
|
|
192
|
+
}: {
|
|
193
|
+
block: CeramicLayoutBlock
|
|
194
|
+
direction: 'ltr' | 'rtl'
|
|
195
|
+
}) {
|
|
196
|
+
const chartType = (
|
|
197
|
+
['line', 'bar', 'area', 'stacked', 'scatter', 'donut'] as ChartType[]
|
|
198
|
+
).includes(block.props.chartType as ChartType)
|
|
199
|
+
? block.props.chartType as ChartType
|
|
200
|
+
: 'line'
|
|
201
|
+
const comparison = typeof block.props.comparison === 'string'
|
|
202
|
+
? block.props.comparison
|
|
203
|
+
: 'previous-period'
|
|
204
|
+
const annotations: ChartAnnotationDefinition[] = comparison === 'benchmark'
|
|
205
|
+
? [{ id: 'benchmark', label: 'Team benchmark', value: 24, tone: 'neutral' }]
|
|
206
|
+
: comparison === 'target'
|
|
207
|
+
? [{ id: 'target', label: 'Weekly target', value: 30, tone: 'neutral' }]
|
|
208
|
+
: []
|
|
209
|
+
const series = comparison === 'previous-period'
|
|
210
|
+
? [currentSeries, previousSeries]
|
|
211
|
+
: [currentSeries]
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<div className="ceramic-chart-panel-block" dir={direction}>
|
|
215
|
+
<ChartLoadBoundary>
|
|
216
|
+
<React.Suspense
|
|
217
|
+
fallback={(
|
|
218
|
+
<div className="ceramic-chart-panel-state" role="status">
|
|
219
|
+
Loading chart preview…
|
|
220
|
+
</div>
|
|
221
|
+
)}
|
|
222
|
+
>
|
|
223
|
+
<LazyAnalyticsChartCard
|
|
224
|
+
annotations={annotations}
|
|
225
|
+
data={chartData}
|
|
226
|
+
description="Completed delivery work by weekday"
|
|
227
|
+
direction={direction}
|
|
228
|
+
series={series}
|
|
229
|
+
showDataTable={block.props.showTable === true}
|
|
230
|
+
state="ready"
|
|
231
|
+
title="Delivery throughput"
|
|
232
|
+
type={chartType}
|
|
233
|
+
/>
|
|
234
|
+
</React.Suspense>
|
|
235
|
+
</ChartLoadBoundary>
|
|
236
|
+
</div>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { getLocaleTextDirection } from '@utopia-studio-design/design-system/Utilities'
|
|
2
|
+
import type {
|
|
3
|
+
ActivityFeedBlockProps,
|
|
4
|
+
ActivityFeedLabels,
|
|
5
|
+
} from './ActivityFeedBlock'
|
|
6
|
+
|
|
7
|
+
export const activityFeedLocaleOptions = ['en-US', 'ar-SA', 'ar-EG', 'ar-AE'] as const
|
|
8
|
+
|
|
9
|
+
export type ActivityFeedLocale = typeof activityFeedLocaleOptions[number]
|
|
10
|
+
export type ActivityFeedDirection = 'inherit' | 'auto' | 'ltr' | 'rtl'
|
|
11
|
+
|
|
12
|
+
export interface ActivityFeedConfiguration {
|
|
13
|
+
density: NonNullable<ActivityFeedBlockProps['density']>
|
|
14
|
+
direction?: 'ltr' | 'rtl'
|
|
15
|
+
groupBy: NonNullable<ActivityFeedBlockProps['groupBy']>
|
|
16
|
+
labels: ActivityFeedLabels
|
|
17
|
+
locale: ActivityFeedLocale
|
|
18
|
+
pageSize: number
|
|
19
|
+
showSources: boolean
|
|
20
|
+
timeZone: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ActivityFeedContext {
|
|
24
|
+
direction: 'ltr' | 'rtl'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const directionModes: ActivityFeedDirection[] = ['inherit', 'auto', 'ltr', 'rtl']
|
|
28
|
+
const densities: ActivityFeedConfiguration['density'][] = ['compact', 'default', 'comfortable']
|
|
29
|
+
const groupings: ActivityFeedConfiguration['groupBy'][] = ['none', 'day', 'source']
|
|
30
|
+
|
|
31
|
+
const englishLabels: ActivityFeedLabels = {
|
|
32
|
+
end: 'You have reached the end of the activity.',
|
|
33
|
+
emptyDescription: 'New updates will appear here when work changes.',
|
|
34
|
+
emptyTitle: 'No activity yet',
|
|
35
|
+
errorDescription: 'Reconnect the activity source and try again.',
|
|
36
|
+
errorTitle: 'Activity unavailable',
|
|
37
|
+
eyebrow: 'Activity',
|
|
38
|
+
invalidTime: 'Time unavailable',
|
|
39
|
+
loadMore: 'Load more',
|
|
40
|
+
loading: 'Loading activity…',
|
|
41
|
+
otherSource: 'Other',
|
|
42
|
+
retry: 'Try again',
|
|
43
|
+
title: 'Recent activity',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const arabicLabels: ActivityFeedLabels = {
|
|
47
|
+
end: 'وصلت إلى نهاية سجل النشاط.',
|
|
48
|
+
emptyDescription: 'ستظهر التحديثات الجديدة هنا عند تغيّر العمل.',
|
|
49
|
+
emptyTitle: 'لا يوجد نشاط بعد',
|
|
50
|
+
errorDescription: 'أعد الاتصال بمصدر النشاط ثم حاول مرة أخرى.',
|
|
51
|
+
errorTitle: 'النشاط غير متاح',
|
|
52
|
+
eyebrow: 'النشاط',
|
|
53
|
+
invalidTime: 'الوقت غير متاح',
|
|
54
|
+
loadMore: 'تحميل المزيد',
|
|
55
|
+
loading: 'جارٍ تحميل النشاط…',
|
|
56
|
+
otherSource: 'أخرى',
|
|
57
|
+
retry: 'حاول مرة أخرى',
|
|
58
|
+
title: 'النشاط الأخير',
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function getActivityFeedLabels(locale: string): ActivityFeedLabels {
|
|
62
|
+
return getLocaleTextDirection(locale) === 'rtl' ? arabicLabels : englishLabels
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resolveActivityFeedConfiguration(
|
|
66
|
+
props: Record<string, unknown>,
|
|
67
|
+
context: ActivityFeedContext,
|
|
68
|
+
): ActivityFeedConfiguration {
|
|
69
|
+
const locale = activityFeedLocaleOptions.includes(props.locale as ActivityFeedLocale)
|
|
70
|
+
? props.locale as ActivityFeedLocale
|
|
71
|
+
: 'en-US'
|
|
72
|
+
const directionMode = directionModes.includes(props.direction as ActivityFeedDirection)
|
|
73
|
+
? props.direction as ActivityFeedDirection
|
|
74
|
+
: 'inherit'
|
|
75
|
+
const direction = directionMode === 'inherit'
|
|
76
|
+
? context.direction
|
|
77
|
+
: directionMode === 'auto' ? getLocaleTextDirection(locale) : directionMode
|
|
78
|
+
const pageSize = Number.isInteger(props.pageSize)
|
|
79
|
+
? Math.min(20, Math.max(2, Number(props.pageSize)))
|
|
80
|
+
: 4
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
density: densities.includes(props.density as ActivityFeedConfiguration['density'])
|
|
84
|
+
? props.density as ActivityFeedConfiguration['density']
|
|
85
|
+
: 'default',
|
|
86
|
+
direction,
|
|
87
|
+
groupBy: groupings.includes(props.groupBy as ActivityFeedConfiguration['groupBy'])
|
|
88
|
+
? props.groupBy as ActivityFeedConfiguration['groupBy']
|
|
89
|
+
: 'day',
|
|
90
|
+
labels: getActivityFeedLabels(locale),
|
|
91
|
+
locale,
|
|
92
|
+
pageSize,
|
|
93
|
+
showSources: props.showSources !== false,
|
|
94
|
+
timeZone: typeof props.timeZone === 'string' && props.timeZone ? props.timeZone : 'UTC',
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ActivityFeedItem } from './ActivityFeedBlock'
|
|
2
|
+
|
|
3
|
+
const activityItems: Record<'en' | 'ar', ActivityFeedItem[]> = {
|
|
4
|
+
en: [
|
|
5
|
+
{ id: 'activity-1', label: 'Calendar block moved after the task list', source: 'Studio', timestamp: '2026-03-09T00:20:00.000Z', tone: 'info' },
|
|
6
|
+
{ id: 'activity-2', label: 'Release QA completed without blockers', source: 'Mina', timestamp: '2026-03-09T01:10:00.000Z', tone: 'positive' },
|
|
7
|
+
{ id: 'activity-3', label: 'Layout JSON published to this workspace', source: 'Agent', timestamp: '2026-03-09T02:30:00.000Z', tone: 'positive' },
|
|
8
|
+
{ id: 'activity-4', label: 'Design-system package data synchronized', source: 'CLI', timestamp: '2026-03-08T06:40:00.000Z', tone: 'neutral' },
|
|
9
|
+
{ id: 'activity-5', label: 'Arabic calendar review requested', source: 'Studio', timestamp: '2026-03-08T08:15:00.000Z', tone: 'warning' },
|
|
10
|
+
],
|
|
11
|
+
ar: [
|
|
12
|
+
{ id: 'activity-1', label: 'نُقلت كتلة التقويم بعد قائمة المهام', source: 'الاستوديو', timestamp: '2026-03-09T00:20:00.000Z', tone: 'info' },
|
|
13
|
+
{ id: 'activity-2', label: 'اكتمل فحص الإصدار من دون عوائق', source: 'مينا', timestamp: '2026-03-09T01:10:00.000Z', tone: 'positive' },
|
|
14
|
+
{ id: 'activity-3', label: 'نُشر ملف تخطيط JSON في مساحة العمل', source: 'الوكيل', timestamp: '2026-03-09T02:30:00.000Z', tone: 'positive' },
|
|
15
|
+
{ id: 'activity-4', label: 'تمت مزامنة بيانات حزمة نظام التصميم', source: 'CLI', timestamp: '2026-03-08T06:40:00.000Z', tone: 'neutral' },
|
|
16
|
+
{ id: 'activity-5', label: 'طُلبت مراجعة التقويم العربي', source: 'الاستوديو', timestamp: '2026-03-08T08:15:00.000Z', tone: 'warning' },
|
|
17
|
+
],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getActivityFeedFixture(locale: string) {
|
|
21
|
+
return activityItems[locale.startsWith('ar') ? 'ar' : 'en']
|
|
22
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
.ceramic-activity-feed {
|
|
2
|
+
display: grid;
|
|
3
|
+
align-content: start;
|
|
4
|
+
gap: var(--space-3);
|
|
5
|
+
min-inline-size: 0;
|
|
6
|
+
block-size: 100%;
|
|
7
|
+
padding: var(--space-4);
|
|
8
|
+
background: var(--surface);
|
|
9
|
+
color: var(--foreground);
|
|
10
|
+
text-align: start;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.ceramic-activity-feed > header,
|
|
14
|
+
.ceramic-activity-feed > header > div,
|
|
15
|
+
.ceramic-activity-items > li,
|
|
16
|
+
.ceramic-activity-copy > div,
|
|
17
|
+
.ceramic-activity-feed > footer {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.ceramic-activity-feed > header {
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
gap: var(--space-3);
|
|
25
|
+
padding-block-end: var(--space-3);
|
|
26
|
+
border-block-end: var(--border-width-hairline) solid var(--border);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.ceramic-activity-feed > header > div {
|
|
30
|
+
align-items: start;
|
|
31
|
+
flex-direction: column;
|
|
32
|
+
gap: var(--space-1);
|
|
33
|
+
min-inline-size: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.ceramic-activity-feed :is(h2, h3, p) {
|
|
37
|
+
margin: 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ceramic-activity-feed > header span {
|
|
41
|
+
color: var(--muted-foreground);
|
|
42
|
+
font-size: var(--font-size-eyebrow);
|
|
43
|
+
font-weight: var(--font-weight-semibold);
|
|
44
|
+
letter-spacing: var(--tracking-eyebrow);
|
|
45
|
+
text-transform: uppercase;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.ceramic-activity-groups {
|
|
49
|
+
display: grid;
|
|
50
|
+
gap: var(--space-4);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.ceramic-activity-groups > section {
|
|
54
|
+
display: grid;
|
|
55
|
+
gap: var(--space-2);
|
|
56
|
+
min-inline-size: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.ceramic-activity-groups h3 {
|
|
60
|
+
color: var(--muted-foreground);
|
|
61
|
+
font-size: var(--font-size-small);
|
|
62
|
+
font-weight: var(--font-weight-semibold);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.ceramic-activity-items {
|
|
66
|
+
display: grid;
|
|
67
|
+
gap: 0;
|
|
68
|
+
margin: 0;
|
|
69
|
+
padding: 0;
|
|
70
|
+
list-style: none;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.ceramic-activity-items > li {
|
|
74
|
+
position: relative;
|
|
75
|
+
align-items: start;
|
|
76
|
+
gap: var(--space-3);
|
|
77
|
+
min-inline-size: 0;
|
|
78
|
+
padding-block: var(--space-2);
|
|
79
|
+
border-block-end: var(--border-width-hairline) solid var(--border);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.ceramic-activity-feed[data-density='compact'] .ceramic-activity-items > li {
|
|
83
|
+
padding-block: var(--space-1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.ceramic-activity-feed[data-density='comfortable'] .ceramic-activity-items > li {
|
|
87
|
+
padding-block: var(--space-3);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.ceramic-activity-marker {
|
|
91
|
+
flex: 0 0 auto;
|
|
92
|
+
margin-block-start: var(--space-1);
|
|
93
|
+
background: var(--muted-foreground);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.ceramic-activity-marker[data-tone='info'] {
|
|
97
|
+
background: var(--primary);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.ceramic-activity-marker[data-tone='positive'] {
|
|
101
|
+
background: var(--success);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.ceramic-activity-marker[data-tone='warning'] {
|
|
105
|
+
background: var(--warning);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.ceramic-activity-marker[data-tone='critical'] {
|
|
109
|
+
background: var(--destructive);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.ceramic-activity-copy {
|
|
113
|
+
display: grid;
|
|
114
|
+
gap: var(--space-1);
|
|
115
|
+
min-inline-size: 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.ceramic-activity-copy p {
|
|
119
|
+
overflow-wrap: anywhere;
|
|
120
|
+
line-height: var(--line-height-body);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.ceramic-activity-copy > div {
|
|
124
|
+
flex-wrap: wrap;
|
|
125
|
+
gap: var(--space-2);
|
|
126
|
+
color: var(--muted-foreground);
|
|
127
|
+
font-size: var(--font-size-small);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.ceramic-activity-state {
|
|
131
|
+
display: grid;
|
|
132
|
+
align-content: center;
|
|
133
|
+
justify-items: start;
|
|
134
|
+
gap: var(--space-2);
|
|
135
|
+
min-block-size: 10rem;
|
|
136
|
+
color: var(--muted-foreground);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.ceramic-activity-state strong {
|
|
140
|
+
color: var(--foreground);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.ceramic-activity-feed > footer {
|
|
144
|
+
justify-content: center;
|
|
145
|
+
min-block-size: var(--control-size-sm);
|
|
146
|
+
color: var(--muted-foreground);
|
|
147
|
+
font-size: var(--font-size-small);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@media (max-width: 28rem) {
|
|
151
|
+
.ceramic-activity-feed {
|
|
152
|
+
padding: var(--space-3);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { getLocaleTextDirection } from '@utopia-studio-design/design-system/Utilities'
|
|
2
|
+
import type {
|
|
3
|
+
AgentStatusBlockProps,
|
|
4
|
+
AgentStatusFilter,
|
|
5
|
+
AgentStatusLabels,
|
|
6
|
+
} from './AgentStatusBlock'
|
|
7
|
+
|
|
8
|
+
export const agentStatusLocaleOptions = ['en-US', 'ar-SA', 'ar-EG', 'ar-AE'] as const
|
|
9
|
+
|
|
10
|
+
export type AgentStatusLocale = typeof agentStatusLocaleOptions[number]
|
|
11
|
+
export type AgentStatusDirection = 'inherit' | 'auto' | 'ltr' | 'rtl'
|
|
12
|
+
|
|
13
|
+
export interface AgentStatusConfiguration {
|
|
14
|
+
density: NonNullable<AgentStatusBlockProps['density']>
|
|
15
|
+
direction?: 'ltr' | 'rtl'
|
|
16
|
+
filter: AgentStatusFilter
|
|
17
|
+
labels: AgentStatusLabels
|
|
18
|
+
locale: AgentStatusLocale
|
|
19
|
+
maxItems: number
|
|
20
|
+
showApprovals: boolean
|
|
21
|
+
timeZone: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface AgentStatusContext {
|
|
25
|
+
direction: 'ltr' | 'rtl'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const directions: AgentStatusDirection[] = ['inherit', 'auto', 'ltr', 'rtl']
|
|
29
|
+
const densities: AgentStatusConfiguration['density'][] = ['compact', 'default', 'comfortable']
|
|
30
|
+
const filters: AgentStatusFilter[] = ['all', 'active', 'attention', 'completed']
|
|
31
|
+
|
|
32
|
+
const englishLabels: AgentStatusLabels = {
|
|
33
|
+
approve: 'Approve',
|
|
34
|
+
approvalRequested: 'Approval requested',
|
|
35
|
+
count: (visible, total) => `${visible} of ${total}`,
|
|
36
|
+
emptyDescription: 'Agent executions will appear here when work begins.',
|
|
37
|
+
emptyTitle: 'No agent executions',
|
|
38
|
+
errorDescription: 'Reconnect the execution source and try again.',
|
|
39
|
+
errorTitle: 'Agent status unavailable',
|
|
40
|
+
eyebrow: 'Agent operations',
|
|
41
|
+
loading: 'Loading agent status…',
|
|
42
|
+
noMatchingDescription: 'Change the semantic filter to review other executions.',
|
|
43
|
+
noMatchingTitle: 'No executions match this view',
|
|
44
|
+
progress: 'Progress',
|
|
45
|
+
reject: 'Reject',
|
|
46
|
+
retry: 'Try again',
|
|
47
|
+
risk: {
|
|
48
|
+
high: 'High risk',
|
|
49
|
+
low: 'Low risk',
|
|
50
|
+
medium: 'Medium risk',
|
|
51
|
+
},
|
|
52
|
+
state: {
|
|
53
|
+
blocked: 'Blocked',
|
|
54
|
+
completed: 'Completed',
|
|
55
|
+
failed: 'Failed',
|
|
56
|
+
queued: 'Queued',
|
|
57
|
+
running: 'Running',
|
|
58
|
+
'waiting-approval': 'Waiting for approval',
|
|
59
|
+
},
|
|
60
|
+
timeUnavailable: 'Update time unavailable',
|
|
61
|
+
title: 'Agent status',
|
|
62
|
+
updated: 'Updated',
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const arabicLabels: AgentStatusLabels = {
|
|
66
|
+
approve: 'موافقة',
|
|
67
|
+
approvalRequested: 'طلب موافقة',
|
|
68
|
+
count: (visible, total) => `${visible} من ${total}`,
|
|
69
|
+
emptyDescription: 'ستظهر عمليات الوكلاء هنا عند بدء العمل.',
|
|
70
|
+
emptyTitle: 'لا توجد عمليات للوكلاء',
|
|
71
|
+
errorDescription: 'أعد الاتصال بمصدر التنفيذ ثم حاول مرة أخرى.',
|
|
72
|
+
errorTitle: 'حالة الوكلاء غير متاحة',
|
|
73
|
+
eyebrow: 'عمليات الوكلاء',
|
|
74
|
+
loading: 'جارٍ تحميل حالة الوكلاء…',
|
|
75
|
+
noMatchingDescription: 'غيّر عامل التصفية الدلالي لمراجعة العمليات الأخرى.',
|
|
76
|
+
noMatchingTitle: 'لا توجد عمليات مطابقة لهذا العرض',
|
|
77
|
+
progress: 'التقدم',
|
|
78
|
+
reject: 'رفض',
|
|
79
|
+
retry: 'حاول مرة أخرى',
|
|
80
|
+
risk: {
|
|
81
|
+
high: 'مخاطر عالية',
|
|
82
|
+
low: 'مخاطر منخفضة',
|
|
83
|
+
medium: 'مخاطر متوسطة',
|
|
84
|
+
},
|
|
85
|
+
state: {
|
|
86
|
+
blocked: 'متوقف',
|
|
87
|
+
completed: 'مكتمل',
|
|
88
|
+
failed: 'فشل',
|
|
89
|
+
queued: 'في قائمة الانتظار',
|
|
90
|
+
running: 'قيد التشغيل',
|
|
91
|
+
'waiting-approval': 'بانتظار الموافقة',
|
|
92
|
+
},
|
|
93
|
+
timeUnavailable: 'وقت التحديث غير متاح',
|
|
94
|
+
title: 'حالة الوكلاء',
|
|
95
|
+
updated: 'آخر تحديث',
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function getAgentStatusLabels(locale: string): AgentStatusLabels {
|
|
99
|
+
return getLocaleTextDirection(locale) === 'rtl' ? arabicLabels : englishLabels
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function resolveAgentStatusConfiguration(
|
|
103
|
+
props: Record<string, unknown>,
|
|
104
|
+
context: AgentStatusContext,
|
|
105
|
+
): AgentStatusConfiguration {
|
|
106
|
+
const locale = agentStatusLocaleOptions.includes(props.locale as AgentStatusLocale)
|
|
107
|
+
? props.locale as AgentStatusLocale
|
|
108
|
+
: 'en-US'
|
|
109
|
+
const directionMode = directions.includes(props.direction as AgentStatusDirection)
|
|
110
|
+
? props.direction as AgentStatusDirection
|
|
111
|
+
: 'inherit'
|
|
112
|
+
const direction = directionMode === 'inherit'
|
|
113
|
+
? context.direction
|
|
114
|
+
: directionMode === 'auto' ? getLocaleTextDirection(locale) : directionMode
|
|
115
|
+
const maxItems = Number.isInteger(props.maxItems)
|
|
116
|
+
? Math.min(12, Math.max(1, Number(props.maxItems)))
|
|
117
|
+
: 6
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
density: densities.includes(props.density as AgentStatusConfiguration['density'])
|
|
121
|
+
? props.density as AgentStatusConfiguration['density']
|
|
122
|
+
: 'default',
|
|
123
|
+
direction,
|
|
124
|
+
filter: filters.includes(props.filter as AgentStatusFilter)
|
|
125
|
+
? props.filter as AgentStatusFilter
|
|
126
|
+
: 'all',
|
|
127
|
+
labels: getAgentStatusLabels(locale),
|
|
128
|
+
locale,
|
|
129
|
+
maxItems,
|
|
130
|
+
showApprovals: props.showApprovals !== false,
|
|
131
|
+
timeZone: typeof props.timeZone === 'string' && props.timeZone ? props.timeZone : 'UTC',
|
|
132
|
+
}
|
|
133
|
+
}
|