@cat-factory/app 0.248.0 → 0.249.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/app/components/board/AddTaskModal.vue +1 -0
- package/app/components/board/nodes/BlockNode.vue +24 -0
- package/app/components/context/ContextAttachmentFields.vue +102 -0
- package/app/components/context/pastedLinkOffer.logic.spec.ts +35 -0
- package/app/components/context/pastedLinkOffer.logic.ts +50 -0
- package/app/components/documents/DocumentSourceConnectModal.vue +50 -0
- package/app/components/documents/SpawnPreviewModal.vue +92 -16
- package/app/components/documents/StartFromDesignModal.vue +237 -0
- package/app/components/layout/AccountDeploymentSettings.vue +122 -0
- package/app/composables/api/documents.ts +17 -6
- package/app/composables/useDocumentSourceConnect.ts +88 -0
- package/app/modular/external-tools.spec.ts +1 -0
- package/app/modular/nav-contributions.spec.ts +2 -0
- package/app/modular/nav-contributions.ts +10 -0
- package/app/modular/nav-gates.ts +4 -0
- package/app/modular/registry.spec.ts +1 -0
- package/app/modular/tutorial-tours.spec.ts +5 -0
- package/app/modular/tutorial-tours.ts +77 -0
- package/app/pages/index.vue +4 -0
- package/app/stores/documents.spec.ts +60 -0
- package/app/stores/documents.ts +52 -24
- package/app/stores/ui/modals.ts +15 -0
- package/i18n/locales/de.json +82 -6
- package/i18n/locales/en.json +82 -6
- package/i18n/locales/es.json +82 -6
- package/i18n/locales/fr.json +82 -6
- package/i18n/locales/he.json +82 -6
- package/i18n/locales/it.json +82 -6
- package/i18n/locales/ja.json +82 -6
- package/i18n/locales/pl.json +82 -6
- package/i18n/locales/tr.json +82 -6
- package/i18n/locales/uk.json +82 -6
- package/package.json +2 -2
|
@@ -152,6 +152,15 @@ export const TUTORIAL_REQUIREMENTS = {
|
|
|
152
152
|
labelKey: 'tutorial.requirements.library',
|
|
153
153
|
met: (gates) => gates.libraryAvailable,
|
|
154
154
|
},
|
|
155
|
+
// The CONNECTION rather than the permission to make one: starting from a design is
|
|
156
|
+
// member-tier, and the frame-header button the tour clicks exists only once some admin has
|
|
157
|
+
// connected a design source. A tour gated on `integrations.manage` would be withheld from
|
|
158
|
+
// exactly the persona it is written for.
|
|
159
|
+
designSource: {
|
|
160
|
+
id: 'design-source',
|
|
161
|
+
labelKey: 'tutorial.requirements.designSource',
|
|
162
|
+
met: (gates) => gates.designSourceConnected,
|
|
163
|
+
},
|
|
155
164
|
} as const satisfies Record<string, TutorialRequirement>
|
|
156
165
|
|
|
157
166
|
export const TUTORIAL_TOURS: readonly TutorialTour[] = [
|
|
@@ -575,6 +584,74 @@ export const TUTORIAL_TOURS: readonly TutorialTour[] = [
|
|
|
575
584
|
},
|
|
576
585
|
],
|
|
577
586
|
},
|
|
587
|
+
{
|
|
588
|
+
id: 'start-from-design',
|
|
589
|
+
order: 55,
|
|
590
|
+
icon: 'i-lucide-frame',
|
|
591
|
+
titleKey: 'tutorial.tours.startFromDesign.title',
|
|
592
|
+
descriptionKey: 'tutorial.tours.startFromDesign.description',
|
|
593
|
+
// The delivery loop as a DESIGNER enters it, which is a different first step from
|
|
594
|
+
// `first-task`: the work starts from a frame in Figma, not from a title someone types. It
|
|
595
|
+
// rides the loop's order (after the run tours, before the platform half) rather than
|
|
596
|
+
// replacing `first-task`, because it ends in the same add-task form and everything after
|
|
597
|
+
// that point is the arc those tours already cover.
|
|
598
|
+
//
|
|
599
|
+
// Offered at launch, unlike the platform tours, and gated on the design source being
|
|
600
|
+
// CONNECTED: on a board with one, this is the everyday loop rather than reference material.
|
|
601
|
+
requires: [
|
|
602
|
+
TUTORIAL_REQUIREMENTS.boardWrite,
|
|
603
|
+
TUTORIAL_REQUIREMENTS.service,
|
|
604
|
+
TUTORIAL_REQUIREMENTS.designSource,
|
|
605
|
+
],
|
|
606
|
+
steps: [
|
|
607
|
+
{
|
|
608
|
+
id: 'intro',
|
|
609
|
+
titleKey: 'tutorial.tours.startFromDesign.steps.intro.title',
|
|
610
|
+
bodyKey: 'tutorial.tours.startFromDesign.steps.intro.body',
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
id: 'open',
|
|
614
|
+
target: 'frame-start-from-design',
|
|
615
|
+
advanceOn: 'target-click',
|
|
616
|
+
placement: 'bottom',
|
|
617
|
+
titleKey: 'tutorial.tours.startFromDesign.steps.open.title',
|
|
618
|
+
bodyKey: 'tutorial.tours.startFromDesign.steps.open.body',
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
id: 'paste',
|
|
622
|
+
target: 'start-from-design-link',
|
|
623
|
+
// Inside the modal the previous click opens.
|
|
624
|
+
waitForTargetMs: 8000,
|
|
625
|
+
placement: 'bottom',
|
|
626
|
+
titleKey: 'tutorial.tours.startFromDesign.steps.paste.title',
|
|
627
|
+
bodyKey: 'tutorial.tours.startFromDesign.steps.paste.body',
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
// Left to the anchor-skip rather than given a `when`: the resolved card appears only
|
|
631
|
+
// once a link has actually been pasted, and someone walking the tour without one in
|
|
632
|
+
// hand should reach the finish card rather than stall on an input they cannot fill.
|
|
633
|
+
id: 'resolved',
|
|
634
|
+
target: 'start-from-design-resolved',
|
|
635
|
+
waitForTargetMs: 8000,
|
|
636
|
+
placement: 'bottom',
|
|
637
|
+
titleKey: 'tutorial.tours.startFromDesign.steps.resolved.title',
|
|
638
|
+
bodyKey: 'tutorial.tours.startFromDesign.steps.resolved.body',
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
id: 'continue',
|
|
642
|
+
target: 'start-from-design-continue',
|
|
643
|
+
advanceOn: 'target-click',
|
|
644
|
+
placement: 'top',
|
|
645
|
+
titleKey: 'tutorial.tours.startFromDesign.steps.continue.title',
|
|
646
|
+
bodyKey: 'tutorial.tours.startFromDesign.steps.continue.body',
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
id: 'finish',
|
|
650
|
+
titleKey: 'tutorial.tours.startFromDesign.steps.finish.title',
|
|
651
|
+
bodyKey: 'tutorial.tours.startFromDesign.steps.finish.body',
|
|
652
|
+
},
|
|
653
|
+
],
|
|
654
|
+
},
|
|
578
655
|
// ---------------------------------------------------------------------------------------
|
|
579
656
|
// The platform half. Ordered after the whole delivery loop so the catalogue reads in the
|
|
580
657
|
// order someone meets these things: learn the loop, then the machinery under it.
|
package/app/pages/index.vue
CHANGED
|
@@ -55,6 +55,9 @@ const DocumentTemplatesModal = defineAsyncComponent(
|
|
|
55
55
|
const SpawnPreviewModal = defineAsyncComponent(
|
|
56
56
|
() => import('~/components/documents/SpawnPreviewModal.vue'),
|
|
57
57
|
)
|
|
58
|
+
const StartFromDesignModal = defineAsyncComponent(
|
|
59
|
+
() => import('~/components/documents/StartFromDesignModal.vue'),
|
|
60
|
+
)
|
|
58
61
|
const BootstrapModal = defineAsyncComponent(
|
|
59
62
|
() => import('~/components/bootstrap/BootstrapModal.vue'),
|
|
60
63
|
)
|
|
@@ -472,6 +475,7 @@ watch(
|
|
|
472
475
|
<DocumentImportModal v-if="ui.documentImport" />
|
|
473
476
|
<DocumentTemplatesModal v-if="ui.documentTemplates" />
|
|
474
477
|
<SpawnPreviewModal v-if="ui.spawnPreview" />
|
|
478
|
+
<StartFromDesignModal v-if="ui.startFromDesign" />
|
|
475
479
|
<BootstrapModal v-if="ui.bootstrapOpen" />
|
|
476
480
|
<AddServiceFromRepoModal v-if="ui.addServiceOpen" />
|
|
477
481
|
<GitHubPanel v-if="ui.githubOpen" />
|
|
@@ -154,3 +154,63 @@ describe('documents store: manual refresh', () => {
|
|
|
154
154
|
expect(store.freshnessFor('figma', 'file1:1-2')).toBeUndefined()
|
|
155
155
|
})
|
|
156
156
|
})
|
|
157
|
+
|
|
158
|
+
describe('documents store: the OAuth and design halves of the source list', () => {
|
|
159
|
+
/** The source listing the probe reads: descriptors PLUS what this deployment can OAuth. */
|
|
160
|
+
function stubSources(oauthSources: string[]) {
|
|
161
|
+
stubApi({
|
|
162
|
+
listDocumentSources: () =>
|
|
163
|
+
Promise.resolve({
|
|
164
|
+
sources: [
|
|
165
|
+
{
|
|
166
|
+
source: 'figma',
|
|
167
|
+
label: 'Figma',
|
|
168
|
+
icon: 'i',
|
|
169
|
+
credentialFields: [],
|
|
170
|
+
refLabel: '',
|
|
171
|
+
refPlaceholder: '',
|
|
172
|
+
oauth: { scopes: ['file_content:read'] },
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
source: 'notion',
|
|
176
|
+
label: 'Notion',
|
|
177
|
+
icon: 'i',
|
|
178
|
+
credentialFields: [],
|
|
179
|
+
refLabel: '',
|
|
180
|
+
refPlaceholder: '',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
oauthSources,
|
|
184
|
+
}),
|
|
185
|
+
listDocumentConnections: () =>
|
|
186
|
+
Promise.resolve({ connections: [{ source: 'figma', label: 'Figma', connectedAt: 1 }] }),
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
it('offers OAuth where the deployment has a registered app', async () => {
|
|
191
|
+
stubSources(['figma'])
|
|
192
|
+
const store = useDocumentsStore()
|
|
193
|
+
await store.probe()
|
|
194
|
+
expect(store.canConnectWithOAuth('figma')).toBe(true)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('withholds it where the SOURCE declares an OAuth half but the deployment registered nothing', async () => {
|
|
198
|
+
// Figma declares the half in both cases; only the registered app decides whether the button
|
|
199
|
+
// is offered. Folded onto the descriptor, this case would render a "Connect with Figma" that
|
|
200
|
+
// can only 503.
|
|
201
|
+
stubSources([])
|
|
202
|
+
const store = useDocumentsStore()
|
|
203
|
+
await store.probe()
|
|
204
|
+
expect(store.descriptorFor('figma')?.oauth).toBeDefined()
|
|
205
|
+
expect(store.canConnectWithOAuth('figma')).toBe(false)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('lists only CONNECTED design sources, so no affordance opens onto a dead end', async () => {
|
|
209
|
+
stubSources(['figma'])
|
|
210
|
+
const store = useDocumentsStore()
|
|
211
|
+
await store.probe()
|
|
212
|
+
// Notion is connectable and not a design source; Figma is both connected and a design one.
|
|
213
|
+
// The classification comes from contracts, so adding a source cannot leave it unclassified.
|
|
214
|
+
expect(store.connectedDesignSources).toEqual(['figma'])
|
|
215
|
+
})
|
|
216
|
+
})
|
package/app/stores/documents.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
} from '~/types/domain'
|
|
15
15
|
import { isConnectableSource } from '@cat-factory/contracts'
|
|
16
16
|
import { useDocumentFreshness } from '~/composables/useDocumentFreshness'
|
|
17
|
+
import { useDocumentSourceConnect } from '~/composables/useDocumentSourceConnect'
|
|
17
18
|
import { useSourceIntegration } from '~/composables/useSourceIntegration'
|
|
18
19
|
import { useUpsertList } from '~/composables/useUpsertList'
|
|
19
20
|
import { useWorkspaceStore } from '~/stores/workspace'
|
|
@@ -32,6 +33,10 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
32
33
|
const api = useApi()
|
|
33
34
|
const workspace = useWorkspaceStore()
|
|
34
35
|
|
|
36
|
+
// What this DEPLOYMENT can OAuth, reported beside the descriptors and read through
|
|
37
|
+
// `useDocumentSourceConnect`, which owns why that is a separate fact from the descriptor's own.
|
|
38
|
+
const oauthSources = ref<DocumentSourceKind[]>([])
|
|
39
|
+
|
|
35
40
|
// Shared opt-in / probe / connections lifecycle (see `useSourceIntegration`).
|
|
36
41
|
const integration = useSourceIntegration<
|
|
37
42
|
DocumentSourceKind,
|
|
@@ -41,16 +46,32 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
41
46
|
enabled: () => !!workspace.workspaceId,
|
|
42
47
|
workspaceId: () => workspace.workspaceId,
|
|
43
48
|
fetch: async () => {
|
|
44
|
-
const [{ sources }, { connections }] = await Promise.all([
|
|
49
|
+
const [{ sources, oauthSources: oauth }, { connections }] = await Promise.all([
|
|
45
50
|
api.listDocumentSources(workspace.requireId()),
|
|
46
51
|
api.listDocumentConnections(workspace.requireId()),
|
|
47
52
|
])
|
|
53
|
+
oauthSources.value = oauth
|
|
48
54
|
return { sources, connections }
|
|
49
55
|
},
|
|
50
56
|
})
|
|
51
57
|
const { available, sources, connections, connectedSources, anyConnected } = integration
|
|
52
58
|
const { descriptorFor, connectionFor, isConnected, probe, ensureProbed } = integration
|
|
53
59
|
|
|
60
|
+
// Connecting a source, and what this board may connect it WITH, in its own collaborator: the
|
|
61
|
+
// question grew several parts when OAuth landed, and none of the store's other concerns (the
|
|
62
|
+
// imported documents, their freshness, the doc-kind role links) read any of them.
|
|
63
|
+
const { canConnectWithOAuth, connectedDesignSources, connect, disconnect, beginOAuthConnect } =
|
|
64
|
+
useDocumentSourceConnect({
|
|
65
|
+
workspaceId: () => workspace.requireId(),
|
|
66
|
+
oauthSources,
|
|
67
|
+
connectedSources,
|
|
68
|
+
onConnected: (conn) => {
|
|
69
|
+
integration.upsertConnection(conn)
|
|
70
|
+
available.value = true
|
|
71
|
+
},
|
|
72
|
+
onDisconnected: (source) => integration.removeConnection(source),
|
|
73
|
+
})
|
|
74
|
+
|
|
54
75
|
const { items: documents, upsert: upsertDoc } = useUpsertList<SourceDocument>({
|
|
55
76
|
key: (d) => `${d.source}:${d.externalId}`,
|
|
56
77
|
prepend: true,
|
|
@@ -76,19 +97,6 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
76
97
|
return documents.value.filter((d) => d.linkedBlockId === blockId)
|
|
77
98
|
}
|
|
78
99
|
|
|
79
|
-
/** Connect the workspace to a source with its credential bag. */
|
|
80
|
-
async function connect(source: DocumentSourceKind, credentials: Record<string, string>) {
|
|
81
|
-
const conn = await api.connectDocumentSource(workspace.requireId(), source, credentials)
|
|
82
|
-
integration.upsertConnection(conn)
|
|
83
|
-
available.value = true
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** Disconnect the workspace from a source. */
|
|
87
|
-
async function disconnect(source: DocumentSourceKind) {
|
|
88
|
-
await api.disconnectDocumentSource(workspace.requireId(), source)
|
|
89
|
-
integration.removeConnection(source)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
100
|
/** Load the imported documents for the workspace (across sources). */
|
|
93
101
|
async function loadDocuments() {
|
|
94
102
|
documents.value = await api.listDocuments(workspace.requireId())
|
|
@@ -125,20 +133,36 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
125
133
|
return results
|
|
126
134
|
}
|
|
127
135
|
|
|
128
|
-
/**
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Preview the board structure a page would expand into (no writes).
|
|
138
|
+
*
|
|
139
|
+
* With `frameId` the preview is TARGET-AWARE: the planner is told which service the work goes
|
|
140
|
+
* inside and proposes its modules and tasks instead of an architecture.
|
|
141
|
+
*/
|
|
142
|
+
function plan(
|
|
143
|
+
source: DocumentSourceKind,
|
|
144
|
+
externalId: string,
|
|
145
|
+
frameId?: string,
|
|
146
|
+
): Promise<DocumentBoardPlan> {
|
|
147
|
+
return api.planDocument(workspace.requireId(), source, {
|
|
148
|
+
externalId,
|
|
149
|
+
...(frameId ? { frameId } : {}),
|
|
150
|
+
})
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
/**
|
|
134
|
-
* Apply a page's structure to the board
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
154
|
+
* Apply a page's structure to the board, then refresh the board snapshot.
|
|
155
|
+
*
|
|
156
|
+
* `frameId` must be the SAME frame the preview was planned for. The endpoint re-plans against
|
|
157
|
+
* it, so a targeted preview and its write agree; sending a frame the preview did not use would
|
|
158
|
+
* flatten a board-wide plan into it and discard the frame titles and types the user approved,
|
|
159
|
+
* which is why this was board-level only until target-aware planning existed.
|
|
139
160
|
*/
|
|
140
|
-
async function spawn(source: DocumentSourceKind, externalId: string) {
|
|
141
|
-
const { result } = await api.spawnDocument(workspace.requireId(), source, {
|
|
161
|
+
async function spawn(source: DocumentSourceKind, externalId: string, frameId?: string) {
|
|
162
|
+
const { result } = await api.spawnDocument(workspace.requireId(), source, {
|
|
163
|
+
externalId,
|
|
164
|
+
...(frameId ? { frameId } : {}),
|
|
165
|
+
})
|
|
142
166
|
await workspace.refresh()
|
|
143
167
|
return result
|
|
144
168
|
}
|
|
@@ -218,6 +242,10 @@ export const useDocumentsStore = defineStore('documents', () => {
|
|
|
218
242
|
return {
|
|
219
243
|
available,
|
|
220
244
|
sources,
|
|
245
|
+
oauthSources,
|
|
246
|
+
canConnectWithOAuth,
|
|
247
|
+
connectedDesignSources,
|
|
248
|
+
beginOAuthConnect,
|
|
221
249
|
connections,
|
|
222
250
|
documents,
|
|
223
251
|
loading,
|
package/app/stores/ui/modals.ts
CHANGED
|
@@ -192,6 +192,11 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
|
192
192
|
// opened standalone, and the modal offers every container on the board.
|
|
193
193
|
const bugHunt = ref<{ source: TaskSourceKind | null; containerId: string | null } | null>(null)
|
|
194
194
|
|
|
195
|
+
// Start-from-design: paste a design link, and the resolved reference is staged onto a new task
|
|
196
|
+
// in `frameId`. `frameId` is always present — the affordance lives on a frame header, and the
|
|
197
|
+
// flow ends in the add-task form, which needs a container to create in.
|
|
198
|
+
const startFromDesign = ref<{ frameId: string } | null>(null)
|
|
199
|
+
|
|
195
200
|
// Add-task modal: the container (service frame or module) a new task is being
|
|
196
201
|
// added to, or null when closed. The user types the title + description; nothing
|
|
197
202
|
// is launched until they explicitly start the created task.
|
|
@@ -266,6 +271,13 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
|
266
271
|
function closeBugHunt() {
|
|
267
272
|
bugHunt.value = null
|
|
268
273
|
}
|
|
274
|
+
function openStartFromDesign(frameId: string) {
|
|
275
|
+
resetHubReturn()
|
|
276
|
+
startFromDesign.value = { frameId }
|
|
277
|
+
}
|
|
278
|
+
function closeStartFromDesign() {
|
|
279
|
+
startFromDesign.value = null
|
|
280
|
+
}
|
|
269
281
|
function openAddTask(containerId: string, prefill: AddTaskPrefill | null = null) {
|
|
270
282
|
addTaskPrefill.value = prefill
|
|
271
283
|
addTaskContainerId.value = containerId
|
|
@@ -301,6 +313,7 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
|
301
313
|
taskConnect,
|
|
302
314
|
taskImport,
|
|
303
315
|
bugHunt,
|
|
316
|
+
startFromDesign,
|
|
304
317
|
addTaskContainerId,
|
|
305
318
|
addTaskPrefill,
|
|
306
319
|
reviewFrictionContext,
|
|
@@ -320,6 +333,8 @@ function createDocumentTaskModals(resetHubReturn: ResetHubReturn) {
|
|
|
320
333
|
closeTaskImport,
|
|
321
334
|
openBugHunt,
|
|
322
335
|
closeBugHunt,
|
|
336
|
+
openStartFromDesign,
|
|
337
|
+
closeStartFromDesign,
|
|
323
338
|
openAddTask,
|
|
324
339
|
closeAddTask,
|
|
325
340
|
openReviewFriction,
|
package/i18n/locales/de.json
CHANGED
|
@@ -2198,6 +2198,18 @@
|
|
|
2198
2198
|
"keysValidation": "Geben Sie die S3-Access Key ID und das Secret ein",
|
|
2199
2199
|
"saved": "Inhaltsspeicher gespeichert",
|
|
2200
2200
|
"saveFailed": "Inhaltsspeicher konnte nicht gespeichert werden"
|
|
2201
|
+
},
|
|
2202
|
+
"figma": {
|
|
2203
|
+
"title": "Figma-App (OAuth)",
|
|
2204
|
+
"description": "Aktiviert den Ablauf „Mit Figma verbinden“. Ohne ihn können Workspaces Figma weiterhin mit einem persönlichen Zugriffstoken verbinden.",
|
|
2205
|
+
"clientId": "Client-ID",
|
|
2206
|
+
"clientSecret": "Client-Secret",
|
|
2207
|
+
"redirectUrl": "Redirect-URL",
|
|
2208
|
+
"validation": "Client-ID, Secret und Redirect-URL eingeben",
|
|
2209
|
+
"saved": "Figma-OAuth gespeichert",
|
|
2210
|
+
"saveFailed": "Figma-OAuth konnte nicht gespeichert werden",
|
|
2211
|
+
"cleared": "Figma-OAuth gelöscht",
|
|
2212
|
+
"clearFailed": "Figma-OAuth konnte nicht gelöscht werden"
|
|
2201
2213
|
}
|
|
2202
2214
|
},
|
|
2203
2215
|
"accountFragments": {
|
|
@@ -3003,7 +3015,8 @@
|
|
|
3003
3015
|
"accessDenied": {
|
|
3004
3016
|
"title": "Zugriff verweigert",
|
|
3005
3017
|
"hint": "Sie haben keinen Zugriff auf das Repository, das diesem Service zugrunde liegt."
|
|
3006
|
-
}
|
|
3018
|
+
},
|
|
3019
|
+
"startFromDesignTitle": "Mit einem Design starten"
|
|
3007
3020
|
},
|
|
3008
3021
|
"decisionBadge": {
|
|
3009
3022
|
"decisionNeeded": "Entscheidung erforderlich",
|
|
@@ -3075,7 +3088,11 @@
|
|
|
3075
3088
|
"attachIssueDisabledConnect": "Verbinden Sie zuerst einen Issue-Tracker (Integrationen)",
|
|
3076
3089
|
"attachIssueDisabledEnable": "Aktivieren Sie zuerst die Issue-Tracker-Integration",
|
|
3077
3090
|
"importsOnAdd": "importiert beim Hinzufügen",
|
|
3078
|
-
"unreadable": "Konnte nicht abgerufen werden: {error}"
|
|
3091
|
+
"unreadable": "Konnte nicht abgerufen werden: {error}",
|
|
3092
|
+
"pastedLink": {
|
|
3093
|
+
"offer": "Diese Beschreibung verlinkt eine {source}-Seite. Als Kontext anhängen?",
|
|
3094
|
+
"attach": "Anhängen"
|
|
3095
|
+
}
|
|
3079
3096
|
},
|
|
3080
3097
|
"providers": {
|
|
3081
3098
|
"presetMismatch": {
|
|
@@ -3921,13 +3938,19 @@
|
|
|
3921
3938
|
"connect": {
|
|
3922
3939
|
"title": "Quelle verbinden",
|
|
3923
3940
|
"sourceFallback": "Quelle",
|
|
3924
|
-
"intro": "
|
|
3941
|
+
"intro": "Verbinde {source}, um Seiten und Designs auf dieses Board zu holen: importieren, in Board-Struktur ausklappen oder als Agent-Kontext an eine Aufgabe hängen.",
|
|
3925
3942
|
"disconnect": "Trennen",
|
|
3926
3943
|
"connect": "Verbinden",
|
|
3927
3944
|
"update": "Verbindung aktualisieren",
|
|
3928
3945
|
"connected": "{source} verbunden",
|
|
3929
3946
|
"disconnected": "{source} getrennt",
|
|
3930
|
-
"connectFailed": "Verbindung nicht möglich"
|
|
3947
|
+
"connectFailed": "Verbindung nicht möglich",
|
|
3948
|
+
"oauth": {
|
|
3949
|
+
"action": "Mit {source} verbinden",
|
|
3950
|
+
"scopes": "Du wirst um folgende Berechtigungen gebeten: {scopes}",
|
|
3951
|
+
"fallback": "Lieber ein Token? Nutze stattdessen die Felder unten.",
|
|
3952
|
+
"failed": "Die Anmeldung bei {source} konnte nicht gestartet werden"
|
|
3953
|
+
}
|
|
3931
3954
|
},
|
|
3932
3955
|
"spawn": {
|
|
3933
3956
|
"title": "Struktur-Vorschau",
|
|
@@ -3942,7 +3965,15 @@
|
|
|
3942
3965
|
"summary": "{frames} · {modules} · {tasks}",
|
|
3943
3966
|
"frameCount": "{count} Frame | {count} Frames",
|
|
3944
3967
|
"moduleCount": "{count} Modul | {count} Module",
|
|
3945
|
-
"taskCount": "{count} Aufgabe | {count} Aufgaben"
|
|
3968
|
+
"taskCount": "{count} Aufgabe | {count} Aufgaben",
|
|
3969
|
+
"reusedCount": "{count} Modul wiederverwendet | {count} Module wiederverwendet",
|
|
3970
|
+
"intoService": "innerhalb des gewählten Service",
|
|
3971
|
+
"target": {
|
|
3972
|
+
"label": "Zu Service hinzufügen",
|
|
3973
|
+
"help": "Ein Design beschreibt Screens, keine Architektur. Der Plan schlägt deshalb Arbeit innerhalb eines bereits vorhandenen Service vor.",
|
|
3974
|
+
"placeholder": "Service wählen",
|
|
3975
|
+
"required": "Wähle den Service, zu dem dieses Design gehört."
|
|
3976
|
+
}
|
|
3946
3977
|
},
|
|
3947
3978
|
"taskDocs": {
|
|
3948
3979
|
"heading": "Kontextdokumente",
|
|
@@ -3975,6 +4006,20 @@
|
|
|
3975
4006
|
"remove": "Entfernen",
|
|
3976
4007
|
"linkFailed": "Die Dokumentverknüpfung konnte nicht aktualisiert werden",
|
|
3977
4008
|
"loadFailed": "Dokumentvorlagen konnten nicht geladen werden"
|
|
4009
|
+
},
|
|
4010
|
+
"startFromDesign": {
|
|
4011
|
+
"title": "Mit einem Design starten",
|
|
4012
|
+
"intro": "Füge den Link zu einem Frame oder einer ganzen Datei ein. Er wird an die neue Aufgabe gehängt, und der Agent liest Layout, Texte, Komponenten und Tokens.",
|
|
4013
|
+
"noSource": "Noch keine Design-Quelle verbunden. Ein Admin kann Figma oder Zeplin unter Integrationen verbinden.",
|
|
4014
|
+
"linkLabel": "Design-Link",
|
|
4015
|
+
"linkPlaceholder": "https://www.figma.com/design/…?node-id=1-2",
|
|
4016
|
+
"checking": "Link wird geprüft…",
|
|
4017
|
+
"trimmed": "Auf die unterstützte Form dieses Links gekürzt.",
|
|
4018
|
+
"widened": "Der Frame {scope} konnte nicht gelesen werden, daher wird die gesamte Datei angehängt.",
|
|
4019
|
+
"unchecked": "Der Link konnte gerade nicht geprüft werden. Er wird beim Anlegen der Aufgabe abgerufen.",
|
|
4020
|
+
"uncheckedAmbiguous": "Der Link konnte nicht geprüft werden, und es ist mehr als eine Design-Quelle verbunden. Zu welcher er gehört, ist damit unbekannt. Bitte erneut versuchen.",
|
|
4021
|
+
"rejected": "Keine verbundene Design-Quelle erkennt diesen Link.",
|
|
4022
|
+
"continue": "Weiter"
|
|
3978
4023
|
}
|
|
3979
4024
|
},
|
|
3980
4025
|
"tasks": {
|
|
@@ -6969,7 +7014,8 @@
|
|
|
6969
7014
|
"library": "Eine in diesem Deployment aktivierte Kontextfragment-Bibliothek",
|
|
6970
7015
|
"failedRun": "Ein fehlgeschlagener Lauf",
|
|
6971
7016
|
"infrastructure": "Ein mit diesem Workspace verbundenes Ausführungs-Backend",
|
|
6972
|
-
"advancedTier": "Der erweiterte Oberflächenmodus"
|
|
7017
|
+
"advancedTier": "Der erweiterte Oberflächenmodus",
|
|
7018
|
+
"designSource": "Eine verbundene Design-Quelle"
|
|
6973
7019
|
},
|
|
6974
7020
|
"overlay": {
|
|
6975
7021
|
"next": "Weiter",
|
|
@@ -7349,6 +7395,36 @@
|
|
|
7349
7395
|
"body": "Ein Entwurf, der angibt, welche davon er nutzt, bekommt im nächsten Schritt ihre vollen Kontrakte. Einer, der nichts angibt, bekommt nichts: dass das hier aktuell bleibt, ist also die Bedingung dafür, dass es funktioniert."
|
|
7350
7396
|
}
|
|
7351
7397
|
}
|
|
7398
|
+
},
|
|
7399
|
+
"startFromDesign": {
|
|
7400
|
+
"title": "Mit einem Design starten",
|
|
7401
|
+
"description": "Mach aus einem Design-Frame eine Aufgabe, die ein Agent bauen kann.",
|
|
7402
|
+
"steps": {
|
|
7403
|
+
"intro": {
|
|
7404
|
+
"title": "Vom Design aus arbeiten",
|
|
7405
|
+
"body": "Wenn die Arbeit im Design-Tool beginnt, ist der Frame das Briefing. Diese Tour hängt einen an eine neue Aufgabe."
|
|
7406
|
+
},
|
|
7407
|
+
"open": {
|
|
7408
|
+
"title": "Mit einem Design starten",
|
|
7409
|
+
"body": "Jeder Service-Frame hat diese Schaltfläche, sobald eine Design-Quelle verbunden ist."
|
|
7410
|
+
},
|
|
7411
|
+
"paste": {
|
|
7412
|
+
"title": "Link einfügen",
|
|
7413
|
+
"body": "Kopiere im Design-Tool den Link zu einem Frame oder einer ganzen Datei und füge ihn hier ein."
|
|
7414
|
+
},
|
|
7415
|
+
"resolved": {
|
|
7416
|
+
"title": "Prüfen, was angehängt wurde",
|
|
7417
|
+
"body": "Der Link wird auf seine unterstützte Form gekürzt. Konnte der Frame selbst nicht gelesen werden, wird stattdessen die ganze Datei angehängt, und genau das steht hier."
|
|
7418
|
+
},
|
|
7419
|
+
"continue": {
|
|
7420
|
+
"title": "Weiter zur Aufgabe",
|
|
7421
|
+
"body": "Das Design liegt an der neuen Aufgabe bereit. Beschreibe das gewünschte Ergebnis und lege sie an."
|
|
7422
|
+
},
|
|
7423
|
+
"finish": {
|
|
7424
|
+
"title": "Bereit für den Lauf",
|
|
7425
|
+
"body": "Der Agent liest Layout, Texte, Komponenten und Tokens des Designs, und jeder Lauf gleicht es erneut mit der Quelle ab."
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7352
7428
|
}
|
|
7353
7429
|
},
|
|
7354
7430
|
"nudge": {
|
package/i18n/locales/en.json
CHANGED
|
@@ -481,7 +481,8 @@
|
|
|
481
481
|
"accessDenied": {
|
|
482
482
|
"title": "Permission denied",
|
|
483
483
|
"hint": "You don't have access to the repository backing this service."
|
|
484
|
-
}
|
|
484
|
+
},
|
|
485
|
+
"startFromDesignTitle": "Start from a design"
|
|
485
486
|
},
|
|
486
487
|
"decisionBadge": {
|
|
487
488
|
"decisionNeeded": "Decision needed",
|
|
@@ -556,7 +557,11 @@
|
|
|
556
557
|
"attachIssueDisabledConnect": "Connect an issue tracker first (Integrations)",
|
|
557
558
|
"attachIssueDisabledEnable": "Enable the issue-tracker integration first",
|
|
558
559
|
"importsOnAdd": "imports on add",
|
|
559
|
-
"unreadable": "Could not be fetched: {error}"
|
|
560
|
+
"unreadable": "Could not be fetched: {error}",
|
|
561
|
+
"pastedLink": {
|
|
562
|
+
"offer": "This description links a {source} page. Attach it as context?",
|
|
563
|
+
"attach": "Attach"
|
|
564
|
+
}
|
|
560
565
|
},
|
|
561
566
|
"errors": {
|
|
562
567
|
"generic": {
|
|
@@ -2138,6 +2143,18 @@
|
|
|
2138
2143
|
"keysValidation": "Enter the S3 access key id and secret",
|
|
2139
2144
|
"saved": "Content storage saved",
|
|
2140
2145
|
"saveFailed": "Could not save content storage"
|
|
2146
|
+
},
|
|
2147
|
+
"figma": {
|
|
2148
|
+
"title": "Figma app (OAuth)",
|
|
2149
|
+
"description": "Enables the \"Connect with Figma\" flow. Without it, workspaces can still connect Figma by pasting a personal access token.",
|
|
2150
|
+
"clientId": "Client ID",
|
|
2151
|
+
"clientSecret": "Client secret",
|
|
2152
|
+
"redirectUrl": "Redirect URL",
|
|
2153
|
+
"validation": "Enter the client id, secret and redirect URL",
|
|
2154
|
+
"saved": "Figma OAuth saved",
|
|
2155
|
+
"saveFailed": "Could not save Figma OAuth",
|
|
2156
|
+
"cleared": "Figma OAuth cleared",
|
|
2157
|
+
"clearFailed": "Could not clear Figma OAuth"
|
|
2141
2158
|
}
|
|
2142
2159
|
},
|
|
2143
2160
|
"accountFragments": {
|
|
@@ -4443,13 +4460,19 @@
|
|
|
4443
4460
|
"connect": {
|
|
4444
4461
|
"title": "Connect source",
|
|
4445
4462
|
"sourceFallback": "Source",
|
|
4446
|
-
"intro": "Connect {source} to
|
|
4463
|
+
"intro": "Connect {source} to bring its pages and designs onto this board: import one, expand it into board structure, or attach it to a task as agent context.",
|
|
4447
4464
|
"disconnect": "Disconnect",
|
|
4448
4465
|
"connect": "Connect",
|
|
4449
4466
|
"update": "Update connection",
|
|
4450
4467
|
"connected": "{source} connected",
|
|
4451
4468
|
"disconnected": "{source} disconnected",
|
|
4452
|
-
"connectFailed": "Could not connect"
|
|
4469
|
+
"connectFailed": "Could not connect",
|
|
4470
|
+
"oauth": {
|
|
4471
|
+
"action": "Connect with {source}",
|
|
4472
|
+
"scopes": "You will be asked to grant: {scopes}",
|
|
4473
|
+
"fallback": "Prefer a token? Use the fields below instead.",
|
|
4474
|
+
"failed": "Could not start the {source} sign-in"
|
|
4475
|
+
}
|
|
4453
4476
|
},
|
|
4454
4477
|
"spawn": {
|
|
4455
4478
|
"title": "Preview structure",
|
|
@@ -4464,7 +4487,15 @@
|
|
|
4464
4487
|
"summary": "{frames} · {modules} · {tasks}",
|
|
4465
4488
|
"frameCount": "{count} frame | {count} frames",
|
|
4466
4489
|
"moduleCount": "{count} module | {count} modules",
|
|
4467
|
-
"taskCount": "{count} task | {count} tasks"
|
|
4490
|
+
"taskCount": "{count} task | {count} tasks",
|
|
4491
|
+
"reusedCount": "{count} module reused | {count} modules reused",
|
|
4492
|
+
"intoService": "inside the selected service",
|
|
4493
|
+
"target": {
|
|
4494
|
+
"label": "Add to service",
|
|
4495
|
+
"help": "A design describes screens, not an architecture, so the plan proposes work inside a service that already exists.",
|
|
4496
|
+
"placeholder": "Pick a service",
|
|
4497
|
+
"required": "Pick the service this design belongs to."
|
|
4498
|
+
}
|
|
4468
4499
|
},
|
|
4469
4500
|
"taskDocs": {
|
|
4470
4501
|
"heading": "Context documents",
|
|
@@ -4497,6 +4528,20 @@
|
|
|
4497
4528
|
"remove": "Remove",
|
|
4498
4529
|
"linkFailed": "Couldn't update the document link",
|
|
4499
4530
|
"loadFailed": "Couldn't load document templates"
|
|
4531
|
+
},
|
|
4532
|
+
"startFromDesign": {
|
|
4533
|
+
"title": "Start from a design",
|
|
4534
|
+
"intro": "Paste a link to a frame or a whole file. It is attached to the new task, and the agent reads its layout, text, components and tokens.",
|
|
4535
|
+
"noSource": "No design source is connected yet. An admin can connect Figma or Zeplin from Integrations.",
|
|
4536
|
+
"linkLabel": "Design link",
|
|
4537
|
+
"linkPlaceholder": "https://www.figma.com/design/…?node-id=1-2",
|
|
4538
|
+
"checking": "Checking the link…",
|
|
4539
|
+
"trimmed": "Trimmed to the supported form of this link.",
|
|
4540
|
+
"widened": "The frame {scope} could not be read, so the whole file is attached instead.",
|
|
4541
|
+
"unchecked": "The link could not be checked right now. It is fetched when the task is created.",
|
|
4542
|
+
"uncheckedAmbiguous": "The link could not be checked, and more than one design source is connected, so which one it belongs to is unknown. Try again.",
|
|
4543
|
+
"rejected": "No connected design source recognises this link.",
|
|
4544
|
+
"continue": "Continue"
|
|
4500
4545
|
}
|
|
4501
4546
|
},
|
|
4502
4547
|
"tasks": {
|
|
@@ -7215,7 +7260,8 @@
|
|
|
7215
7260
|
"library": "The context fragment library enabled on this deployment",
|
|
7216
7261
|
"failedRun": "A run that failed",
|
|
7217
7262
|
"infrastructure": "An execution backend connected to this workspace",
|
|
7218
|
-
"advancedTier": "The advanced interface mode"
|
|
7263
|
+
"advancedTier": "The advanced interface mode",
|
|
7264
|
+
"designSource": "A connected design source"
|
|
7219
7265
|
},
|
|
7220
7266
|
"overlay": {
|
|
7221
7267
|
"next": "Next",
|
|
@@ -7604,6 +7650,36 @@
|
|
|
7604
7650
|
"body": "A design that declares which of these it uses gets their full contracts in the next step. One that declares nothing gets nothing, so keeping this current is what makes it work."
|
|
7605
7651
|
}
|
|
7606
7652
|
}
|
|
7653
|
+
},
|
|
7654
|
+
"startFromDesign": {
|
|
7655
|
+
"title": "Start from a design",
|
|
7656
|
+
"description": "Turn a design frame into a task an agent can build.",
|
|
7657
|
+
"steps": {
|
|
7658
|
+
"intro": {
|
|
7659
|
+
"title": "Work from the design",
|
|
7660
|
+
"body": "When the work starts in a design tool, the frame is the brief. This tour attaches one to a new task."
|
|
7661
|
+
},
|
|
7662
|
+
"open": {
|
|
7663
|
+
"title": "Start from a design",
|
|
7664
|
+
"body": "Every service frame carries this button once a design source is connected."
|
|
7665
|
+
},
|
|
7666
|
+
"paste": {
|
|
7667
|
+
"title": "Paste the link",
|
|
7668
|
+
"body": "Copy the link to a frame or a whole file from your design tool and paste it here."
|
|
7669
|
+
},
|
|
7670
|
+
"resolved": {
|
|
7671
|
+
"title": "Check what was attached",
|
|
7672
|
+
"body": "The link is trimmed to its supported form. If the frame itself could not be read, the whole file is attached instead and it says so here."
|
|
7673
|
+
},
|
|
7674
|
+
"continue": {
|
|
7675
|
+
"title": "Carry on to the task",
|
|
7676
|
+
"body": "The design is staged on the new task. Describe the outcome you want and create it."
|
|
7677
|
+
},
|
|
7678
|
+
"finish": {
|
|
7679
|
+
"title": "Ready to run",
|
|
7680
|
+
"body": "The agent reads the design's layout, text, components and tokens, and every run re-checks it against the source."
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7607
7683
|
}
|
|
7608
7684
|
},
|
|
7609
7685
|
"nudge": {
|