@agent-native/core 0.106.1 → 0.106.2

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 (51) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +6 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/artifact-response.ts +84 -0
  5. package/corpus/core/src/agent/thread-data-builder.ts +1 -1
  6. package/corpus/core/src/integrations/webhook-handler.ts +41 -5
  7. package/corpus/core/src/server/agent-chat-plugin.ts +7 -2
  8. package/corpus/templates/clips/app/components/capture-install-options.tsx +60 -5
  9. package/corpus/templates/clips/app/components/library/library-layout.tsx +9 -1
  10. package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +4 -4
  11. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  12. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  13. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  14. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  15. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  16. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  17. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  18. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  19. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  20. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  21. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  22. package/corpus/templates/clips/app/lib/capture-install-options.ts +89 -4
  23. package/corpus/templates/clips/app/routes/_app.dictate.tsx +10 -1
  24. package/corpus/templates/clips/app/routes/record.tsx +1 -0
  25. package/corpus/templates/clips/app/routes/share.$shareId.tsx +7 -0
  26. package/corpus/templates/clips/changelog/2026-07-16-the-desktop-app-button-now-shows-open-desktop-app-and-launch.md +6 -0
  27. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +5 -0
  28. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +1 -0
  29. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +41 -6
  30. package/corpus/templates/clips/desktop/src-tauri/tauri.conf.json +5 -0
  31. package/corpus/templates/content/.agents/skills/content/SKILL.md +31 -0
  32. package/corpus/templates/content/changelog/2026-07-16-slack-corrections-now-preserve-newer-content-values-unless-y.md +6 -0
  33. package/dist/a2a/artifact-response.d.ts +9 -0
  34. package/dist/a2a/artifact-response.d.ts.map +1 -1
  35. package/dist/a2a/artifact-response.js +68 -0
  36. package/dist/a2a/artifact-response.js.map +1 -1
  37. package/dist/agent/thread-data-builder.js +1 -1
  38. package/dist/agent/thread-data-builder.js.map +1 -1
  39. package/dist/collab/awareness.d.ts +2 -2
  40. package/dist/collab/awareness.d.ts.map +1 -1
  41. package/dist/collab/struct-routes.d.ts +1 -1
  42. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  43. package/dist/integrations/webhook-handler.js +24 -5
  44. package/dist/integrations/webhook-handler.js.map +1 -1
  45. package/dist/observability/routes.d.ts +5 -5
  46. package/dist/secrets/routes.d.ts +9 -9
  47. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  48. package/dist/server/agent-chat-plugin.js +3 -1
  49. package/dist/server/agent-chat-plugin.js.map +1 -1
  50. package/dist/server/transcribe-voice.d.ts +1 -1
  51. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 2283
31
- - template files: 6062
31
+ - template files: 6064
@@ -1,5 +1,11 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.106.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 7fb8e89: Return a verified artifact receipt when an integration mutation succeeds without a final model summary.
8
+
3
9
  ## 0.106.1
4
10
 
5
11
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.106.1",
3
+ "version": "0.106.2",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -3,6 +3,8 @@ import { createHmac, timingSafeEqual } from "node:crypto";
3
3
  export interface A2AToolResultSummary {
4
4
  tool: string;
5
5
  result: string;
6
+ isError?: boolean;
7
+ completedSideEffect?: boolean;
6
8
  }
7
9
 
8
10
  export interface A2AArtifactResponseOptions {
@@ -34,6 +36,7 @@ const ARTIFACT_IDENTITY_WRITE_TOOLS = new Set([
34
36
  "add-database-item",
35
37
  "create-document",
36
38
  "update-document",
39
+ "set-document-property",
37
40
  "create-deck",
38
41
  "duplicate-deck",
39
42
  "add-slide",
@@ -545,6 +548,8 @@ function collectArtifacts(results: A2AToolResultSummary[]): {
545
548
  const forms = new Map<string, CreatedFormArtifact>();
546
549
 
547
550
  for (const toolResult of results) {
551
+ if (toolResult.isError === true || toolResult.completedSideEffect === false)
552
+ continue;
548
553
  if (toolResult.tool === "call-agent") {
549
554
  for (const artifact of parseDownstreamArtifactBlock(toolResult.result)) {
550
555
  if (artifact.kind === "deck") {
@@ -632,6 +637,7 @@ function collectArtifacts(results: A2AToolResultSummary[]): {
632
637
  toolResult.tool === "create-document" ||
633
638
  toolResult.tool === "update-document"
634
639
  ) {
640
+ if (parsed.conflict === true) continue;
635
641
  const id = stringValue(parsed.id);
636
642
  if (id) {
637
643
  documents.set(id, {
@@ -643,6 +649,17 @@ function collectArtifacts(results: A2AToolResultSummary[]): {
643
649
  continue;
644
650
  }
645
651
 
652
+ if (toolResult.tool === "set-document-property") {
653
+ const id = stringValue(parsed.documentId);
654
+ if (id) {
655
+ documents.set(id, {
656
+ id,
657
+ url: stringValue(parsed.url) ?? stringValue(parsed.urlPath),
658
+ });
659
+ }
660
+ continue;
661
+ }
662
+
646
663
  if (
647
664
  toolResult.tool === "get-document" ||
648
665
  toolResult.tool === "get-content-document"
@@ -872,6 +889,8 @@ export function extractA2AArtifactIdentities(
872
889
  };
873
890
 
874
891
  for (const result of results) {
892
+ if (result.isError === true || result.completedSideEffect === false)
893
+ continue;
875
894
  if (result.tool === "call-agent") {
876
895
  for (const identity of persistedArtifactIdentitiesFromMarker(
877
896
  result.result,
@@ -1540,3 +1559,68 @@ export function buildA2ARecoverableArtifactMessage(
1540
1559
  ...lines,
1541
1560
  ].join("\n");
1542
1561
  }
1562
+
1563
+ function mutationReceiptUrl(
1564
+ identity: A2AArtifactIdentity,
1565
+ baseUrl: string | undefined,
1566
+ ): string | undefined {
1567
+ if (
1568
+ identity.sourceAction === "call-agent" &&
1569
+ (!identity.url || identity.url.startsWith("/"))
1570
+ ) {
1571
+ return undefined;
1572
+ }
1573
+ if (identity.url) {
1574
+ return identity.url.startsWith("/")
1575
+ ? artifactUrl(baseUrl, identity.url)
1576
+ : identity.url;
1577
+ }
1578
+
1579
+ const path =
1580
+ identity.resourceType === "document"
1581
+ ? `/page/${identity.id}`
1582
+ : identity.resourceType === "deck"
1583
+ ? `/deck/${identity.id}`
1584
+ : identity.resourceType === "dashboard"
1585
+ ? `/adhoc/${identity.id}`
1586
+ : identity.resourceType === "analysis"
1587
+ ? `/analyses/${identity.id}`
1588
+ : identity.resourceType === "image"
1589
+ ? `/image/${identity.id}`
1590
+ : identity.resourceType === "design"
1591
+ ? `/design/${identity.id}`
1592
+ : undefined;
1593
+ return path ? artifactUrl(baseUrl, path) : undefined;
1594
+ }
1595
+
1596
+ /**
1597
+ * Build a bounded participant-facing receipt from authenticated artifact writes.
1598
+ * Unlike generic artifact recovery, this only trusts identities extracted from
1599
+ * successful write actions (or a signed downstream write ledger), so a read or
1600
+ * an unverified URL cannot be rounded up to a successful mutation.
1601
+ */
1602
+ export function buildA2AVerifiedMutationReceipt(
1603
+ toolResults: A2AToolResultSummary[],
1604
+ options: A2AArtifactResponseOptions = {},
1605
+ ): string | null {
1606
+ const baseUrl = normalizeBaseUrl(options.baseUrl);
1607
+ const identities = extractA2AArtifactIdentities(toolResults);
1608
+ if (identities.length === 0) return null;
1609
+
1610
+ const lines = identities.map((identity) => {
1611
+ const label =
1612
+ identity.resourceType.charAt(0).toUpperCase() +
1613
+ identity.resourceType.slice(1);
1614
+ const url = mutationReceiptUrl(identity, baseUrl);
1615
+ return url
1616
+ ? `- ${label}: ${url} (ID: ${identity.id})`
1617
+ : `- ${label} ID: ${identity.id}`;
1618
+ });
1619
+
1620
+ return [
1621
+ "A verified change was saved, but I couldn't generate the detailed summary.",
1622
+ "",
1623
+ "Saved artifacts:",
1624
+ ...lines,
1625
+ ].join("\n");
1626
+ }
@@ -719,7 +719,7 @@ export function threadMessageTextForEngine(message: any): string {
719
719
 
720
720
  const context = [
721
721
  "<integration_artifact_context>",
722
- "Trusted action history for this conversation. Resource IDs remain stable if participants rename the resource. Use these IDs when a follow-up refers to an earlier artifact, while still deciding from the request whether to update, add, supersede, or create.",
722
+ "Trusted action history for this conversation. Resource IDs remain stable if participants rename the resource. Fields such as titleAtAction are historical aliases from the time of that action, not current resource state. Use stable IDs to locate an earlier artifact, read its current state before changing it, and omit fields the user did not explicitly ask to change while still deciding whether to update, add, supersede, or create.",
723
723
  promptSafeJson(artifacts),
724
724
  "</integration_artifact_context>",
725
725
  ].join("\n");
@@ -2,6 +2,7 @@ import type { H3Event } from "h3";
2
2
 
3
3
  import {
4
4
  appendA2AArtifactLinks,
5
+ buildA2AVerifiedMutationReceipt,
5
6
  extractA2AArtifactIdentities,
6
7
  type A2AToolResultSummary,
7
8
  } from "../a2a/artifact-response.js";
@@ -83,7 +84,13 @@ const DEFERRED_RESPONSE_MAX_HANDLER_MS = 2_500;
83
84
  const EMPTY_INTEGRATION_RESPONSE_MESSAGE =
84
85
  "The model finished without a visible answer. Try again, or open the thread in Dispatch to inspect the run.";
85
86
 
86
- type ToolDoneEvent = { type: "tool_done"; tool: string; result: string };
87
+ type ToolDoneEvent = {
88
+ type: "tool_done";
89
+ tool: string;
90
+ result: string;
91
+ isError?: boolean;
92
+ completedSideEffect?: boolean;
93
+ };
87
94
 
88
95
  /**
89
96
  * Build a stable per-event dedup key from the incoming message. The same
@@ -204,6 +211,25 @@ function collectToolResultSummaries(
204
211
  return completedRun.events
205
212
  .map((runEvent) => runEvent.event)
206
213
  .filter((event): event is ToolDoneEvent => event.type === "tool_done")
214
+ .map((event) => ({
215
+ tool: event.tool,
216
+ result: event.result,
217
+ isError: event.isError,
218
+ completedSideEffect: event.completedSideEffect,
219
+ }));
220
+ }
221
+
222
+ function collectCompletedMutationToolResultSummaries(
223
+ completedRun: ActiveRun,
224
+ ): A2AToolResultSummary[] {
225
+ return completedRun.events
226
+ .map((runEvent) => runEvent.event)
227
+ .filter(
228
+ (event): event is ToolDoneEvent =>
229
+ event.type === "tool_done" &&
230
+ event.completedSideEffect === true &&
231
+ event.isError !== true,
232
+ )
207
233
  .map((event) => ({ tool: event.tool, result: event.result }));
208
234
  }
209
235
 
@@ -923,6 +949,18 @@ async function processIncomingMessage(
923
949
  queuedA2AContinuation &&
924
950
  isQueuedA2AContinuationDeferral(responseText);
925
951
 
952
+ // Compute trusted tool receipts before choosing the empty-answer
953
+ // fallback. A completed write must not be reported as though nothing
954
+ // happened merely because the model ran out of time before its prose
955
+ // summary. Read-only and unverified tool results do not qualify.
956
+ const baseUrl = process.env.APP_URL || process.env.URL || "";
957
+ const appBaseUrl = baseUrl ? withConfiguredAppBasePath(baseUrl) : "";
958
+ const toolResults = collectToolResultSummaries(completedRun);
959
+ const verifiedMutationReceipt = buildA2AVerifiedMutationReceipt(
960
+ collectCompletedMutationToolResultSummaries(completedRun),
961
+ { baseUrl: appBaseUrl || undefined },
962
+ );
963
+
926
964
  // If the run errored OR produced no text, post a graceful fallback so
927
965
  // the user isn't left wondering whether the bot saw their message.
928
966
  // Common case: an A2A delegation timed out and the agent loop bailed
@@ -953,7 +991,8 @@ async function processIncomingMessage(
953
991
  "If it was a complex analytics question, opening the analytics app " +
954
992
  "directly is the most reliable way to get an answer right now.";
955
993
  } else {
956
- responseText = EMPTY_INTEGRATION_RESPONSE_MESSAGE;
994
+ responseText =
995
+ verifiedMutationReceipt ?? EMPTY_INTEGRATION_RESPONSE_MESSAGE;
957
996
  }
958
997
  }
959
998
  if (approval?.type === "approval_required") {
@@ -965,9 +1004,6 @@ async function processIncomingMessage(
965
1004
  // platforms with rich blocks (Slack) can render a button instead
966
1005
  // of inlining a `<url|text>` link that auto-unfurls into a giant
967
1006
  // preview card.
968
- const baseUrl = process.env.APP_URL || process.env.URL || "";
969
- const appBaseUrl = baseUrl ? withConfiguredAppBasePath(baseUrl) : "";
970
- const toolResults = collectToolResultSummaries(completedRun);
971
1007
  if (!suppressPlatformReply) {
972
1008
  responseText = appendA2AArtifactLinks(responseText, toolResults, {
973
1009
  baseUrl: appBaseUrl || undefined,
@@ -13,7 +13,10 @@ import {
13
13
  type H3Event,
14
14
  } from "h3";
15
15
 
16
- import { buildA2ARecoverableArtifactMessage } from "../a2a/artifact-response.js";
16
+ import {
17
+ buildA2ARecoverableArtifactMessage,
18
+ type A2AToolResultSummary,
19
+ } from "../a2a/artifact-response.js";
17
20
  import {
18
21
  hasConfiguredA2ASecret,
19
22
  isLoopbackAddress,
@@ -1463,7 +1466,7 @@ export function createAgentChatPlugin(
1463
1466
  // Run the SAME agent loop, then extract the final answer from the
1464
1467
  // event stream so pre-tool narration never leaks as the A2A result.
1465
1468
  const a2aEvents: AgentChatEvent[] = [];
1466
- const a2aToolResults: Array<{ tool: string; result: string }> = [];
1469
+ const a2aToolResults: A2AToolResultSummary[] = [];
1467
1470
  let lastRecoverableArtifactText = "";
1468
1471
  const controller = new AbortController();
1469
1472
 
@@ -1498,6 +1501,8 @@ export function createAgentChatPlugin(
1498
1501
  a2aToolResults.push({
1499
1502
  tool: event.tool,
1500
1503
  result: event.result,
1504
+ isError: event.isError,
1505
+ completedSideEffect: event.completedSideEffect,
1501
1506
  });
1502
1507
  const recoverableArtifactText =
1503
1508
  buildA2ARecoverableArtifactMessage(a2aToolResults, {
@@ -7,7 +7,7 @@ import {
7
7
  IconDeviceDesktop,
8
8
  IconExternalLink,
9
9
  } from "@tabler/icons-react";
10
- import { type ReactNode } from "react";
10
+ import { type ReactNode, useSyncExternalStore } from "react";
11
11
 
12
12
  import { Button, type ButtonProps } from "@/components/ui/button";
13
13
  import {
@@ -16,11 +16,24 @@ import {
16
16
  PopoverTrigger,
17
17
  } from "@/components/ui/popover";
18
18
  import {
19
+ attemptOpenDesktopApp,
19
20
  clipsChromeExtensionEnabled,
20
21
  clipsChromeExtensionUrl,
22
+ hasDownloadedDesktopApp,
23
+ subscribeDownloaded,
21
24
  } from "@/lib/capture-install-options";
22
25
  import { cn } from "@/lib/utils";
23
26
 
27
+ // SSR snapshot is always false; same-tab markDesktopAppDownloaded() notifies
28
+ // subscribers so mounted CTAs flip to "Open" without a reload.
29
+ function useHasDownloadedDesktopApp(): boolean {
30
+ return useSyncExternalStore(
31
+ subscribeDownloaded,
32
+ hasDownloadedDesktopApp,
33
+ () => false,
34
+ );
35
+ }
36
+
24
37
  type PopoverPlacement = {
25
38
  align?: "start" | "center" | "end";
26
39
  side?: "top" | "right" | "bottom" | "left";
@@ -29,11 +42,15 @@ type PopoverPlacement = {
29
42
  type CaptureInstallButtonProps = Omit<ButtonProps, "asChild"> &
30
43
  PopoverPlacement & {
31
44
  children: ReactNode;
45
+ /** Label shown once the desktop app has been downloaded. */
46
+ downloadedChildren?: ReactNode;
32
47
  desktopHref?: string;
33
48
  };
34
49
 
35
50
  type CaptureInstallInlineLinkProps = PopoverPlacement & {
36
51
  children: ReactNode;
52
+ /** Label shown once the desktop app has been downloaded. */
53
+ downloadedChildren?: ReactNode;
37
54
  className?: string;
38
55
  desktopHref?: string;
39
56
  };
@@ -110,16 +127,37 @@ function InstallOptionsContent({ desktopHref = "/download" }) {
110
127
 
111
128
  export function CaptureInstallButton({
112
129
  children,
130
+ downloadedChildren,
113
131
  className,
114
132
  desktopHref = "/download",
115
133
  align = "end",
116
134
  side = "bottom",
117
135
  ...buttonProps
118
136
  }: CaptureInstallButtonProps) {
137
+ const downloaded = useHasDownloadedDesktopApp();
138
+ const label = downloaded ? (downloadedChildren ?? children) : children;
139
+
140
+ if (downloaded) {
141
+ const { onClick, ...restButtonProps } = buttonProps;
142
+ return (
143
+ <Button
144
+ className={className}
145
+ {...restButtonProps}
146
+ onClick={(event) => {
147
+ onClick?.(event);
148
+ if (event.defaultPrevented) return;
149
+ attemptOpenDesktopApp(desktopHref);
150
+ }}
151
+ >
152
+ {label}
153
+ </Button>
154
+ );
155
+ }
156
+
119
157
  if (!clipsChromeExtensionEnabled) {
120
158
  return (
121
159
  <Button asChild className={className} {...buttonProps}>
122
- <a href={appPath(desktopHref)}>{children}</a>
160
+ <a href={appPath(desktopHref)}>{label}</a>
123
161
  </Button>
124
162
  );
125
163
  }
@@ -128,7 +166,7 @@ export function CaptureInstallButton({
128
166
  <Popover>
129
167
  <PopoverTrigger asChild>
130
168
  <Button className={className} {...buttonProps}>
131
- {children}
169
+ {label}
132
170
  <IconChevronDown className="h-3.5 w-3.5" />
133
171
  </Button>
134
172
  </PopoverTrigger>
@@ -141,15 +179,32 @@ export function CaptureInstallButton({
141
179
 
142
180
  export function CaptureInstallInlineLink({
143
181
  children,
182
+ downloadedChildren,
144
183
  className,
145
184
  desktopHref = "/download",
146
185
  align = "start",
147
186
  side = "bottom",
148
187
  }: CaptureInstallInlineLinkProps) {
188
+ const downloaded = useHasDownloadedDesktopApp();
189
+
190
+ const label = downloaded ? (downloadedChildren ?? children) : children;
191
+
192
+ if (downloaded) {
193
+ return (
194
+ <button
195
+ type="button"
196
+ onClick={() => attemptOpenDesktopApp(desktopHref)}
197
+ className={cn("cursor-pointer", className)}
198
+ >
199
+ {label}
200
+ </button>
201
+ );
202
+ }
203
+
149
204
  if (!clipsChromeExtensionEnabled) {
150
205
  return (
151
206
  <a href={appPath(desktopHref)} className={className}>
152
- {children}
207
+ {label}
153
208
  </a>
154
209
  );
155
210
  }
@@ -158,7 +213,7 @@ export function CaptureInstallInlineLink({
158
213
  <Popover>
159
214
  <PopoverTrigger asChild>
160
215
  <button type="button" className={cn("cursor-pointer", className)}>
161
- {children}
216
+ {label}
162
217
  </button>
163
218
  </PopoverTrigger>
164
219
  <PopoverContent align={align} side={side} className="w-80 p-3">
@@ -528,7 +528,15 @@ export function LibraryLayout({ children }: LibraryLayoutProps) {
528
528
  <>
529
529
  <div className="shrink-0 space-y-1.5 px-2 py-1.5">
530
530
  {shouldShowSidebarLink && (
531
- <CaptureInstallInlineLink className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs text-foreground hover:bg-accent/60">
531
+ <CaptureInstallInlineLink
532
+ className="flex w-full items-center gap-2 rounded px-2 py-1.5 text-xs text-foreground hover:bg-accent/60"
533
+ downloadedChildren={
534
+ <>
535
+ <IconAppWindow className="h-4 w-4" />
536
+ {t("captureInstall.openDesktopApp")}
537
+ </>
538
+ }
539
+ >
532
540
  <IconAppWindow className="h-4 w-4" />
533
541
  {t("navigation.desktopCta")}
534
542
  </CaptureInstallInlineLink>
@@ -2,8 +2,8 @@ import { useCallback, useEffect, useState } from "react";
2
2
 
3
3
  import { useIsMobile } from "@/hooks/use-mobile";
4
4
  import {
5
- hasDownloadedDesktopApp,
6
- markDesktopAppDownloaded,
5
+ hasDismissedDesktopPromo,
6
+ markDesktopPromoDismissed,
7
7
  } from "@/lib/capture-install-options";
8
8
 
9
9
  function detectDesktopApp(): boolean {
@@ -27,12 +27,12 @@ export function useDesktopPromo() {
27
27
 
28
28
  useEffect(() => {
29
29
  setIsDesktopApp(detectDesktopApp());
30
- setDismissed(hasDownloadedDesktopApp());
30
+ setDismissed(hasDismissedDesktopPromo());
31
31
  }, []);
32
32
 
33
33
  const dismiss = useCallback(() => {
34
34
  setDismissed(true);
35
- markDesktopAppDownloaded();
35
+ markDesktopPromoDismissed();
36
36
  }, []);
37
37
 
38
38
  return {
@@ -1106,6 +1106,7 @@ const messages = {
1106
1106
  desktopTitle: "Desktop app (مترجم)",
1107
1107
  desktopDescription:
1108
1108
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (مترجم)",
1109
+ openDesktopApp: "Open desktop app (مترجم)",
1109
1110
  },
1110
1111
  editableTitle: {
1111
1112
  untitled: "Untitled Clip (مترجم)",
@@ -1132,6 +1132,7 @@ Alle sichtbaren Änderungen für Clips-Nutzer werden hier dokumentiert. Du kanns
1132
1132
  desktopTitle: "Desktop app (Lokalisiert)",
1133
1133
  desktopDescription:
1134
1134
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (Lokalisiert)",
1135
+ openDesktopApp: "Open desktop app (Lokalisiert)",
1135
1136
  },
1136
1137
  editableTitle: {
1137
1138
  untitled: "Untitled Clip (Lokalisiert)",
@@ -1102,6 +1102,7 @@ All notable user-facing changes to Clips are documented here. Open it any time f
1102
1102
  desktopTitle: "Desktop app",
1103
1103
  desktopDescription:
1104
1104
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures.",
1105
+ openDesktopApp: "Open desktop app",
1105
1106
  },
1106
1107
  editableTitle: {
1107
1108
  untitled: "Untitled Clip",
@@ -1125,6 +1125,7 @@ Todos los cambios visibles para los usuarios de Clips se documentan aquí. Puede
1125
1125
  desktopTitle: "Desktop app (Localizado)",
1126
1126
  desktopDescription:
1127
1127
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (Localizado)",
1128
+ openDesktopApp: "Open desktop app (Localizado)",
1128
1129
  },
1129
1130
  editableTitle: {
1130
1131
  untitled: "Untitled Clip (Localizado)",
@@ -1127,6 +1127,7 @@ Tous les changements visibles par les utilisateurs de Clips sont documentés ici
1127
1127
  desktopTitle: "Desktop app (Localisé)",
1128
1128
  desktopDescription:
1129
1129
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (Localisé)",
1130
+ openDesktopApp: "Open desktop app (Localisé)",
1130
1131
  },
1131
1132
  editableTitle: {
1132
1133
  untitled: "Untitled Clip (Localisé)",
@@ -1093,6 +1093,7 @@ Clips में उपयोगकर्ताओं को दिखने व
1093
1093
  desktopTitle: "Desktop app (स्थानीयकृत)",
1094
1094
  desktopDescription:
1095
1095
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (स्थानीयकृत)",
1096
+ openDesktopApp: "Open desktop app (स्थानीयकृत)",
1096
1097
  },
1097
1098
  editableTitle: {
1098
1099
  untitled: "Untitled Clip (स्थानीयकृत)",
@@ -1114,6 +1114,7 @@ Clips のユーザー向けの主な変更はここに記録されます。コ
1114
1114
  desktopTitle: "Desktop app (ローカライズ済み)",
1115
1115
  desktopDescription:
1116
1116
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (ローカライズ済み)",
1117
+ openDesktopApp: "Open desktop app (ローカライズ済み)",
1117
1118
  },
1118
1119
  editableTitle: {
1119
1120
  untitled: "Untitled Clip (ローカライズ済み)",
@@ -1103,6 +1103,7 @@ Clips의 모든 사용자 대상 변경 사항은 여기에 기록됩니다. 명
1103
1103
  desktopTitle: "Desktop app (현지화됨)",
1104
1104
  desktopDescription:
1105
1105
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (현지화됨)",
1106
+ openDesktopApp: "Open desktop app (현지화됨)",
1106
1107
  },
1107
1108
  editableTitle: {
1108
1109
  untitled: "Untitled Clip (현지화됨)",
@@ -1121,6 +1121,7 @@ Todas as mudanças visíveis para usuários do Clips são documentadas aqui. Voc
1121
1121
  desktopTitle: "Desktop app (Localizado)",
1122
1122
  desktopDescription:
1123
1123
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (Localizado)",
1124
+ openDesktopApp: "Open desktop app (Localizado)",
1124
1125
  },
1125
1126
  editableTitle: {
1126
1127
  untitled: "Untitled Clip (Localizado)",
@@ -1064,6 +1064,7 @@ Clips 中所有面向用户的重要更改都会记录在这里。你可以随
1064
1064
  desktopTitle: "Desktop app (已本地化)",
1065
1065
  desktopDescription:
1066
1066
  "Most seamless for global shortcuts, menu-bar recording, meetings, and repeat captures. (已本地化)",
1067
+ openDesktopApp: "Open desktop app (已本地化)",
1067
1068
  },
1068
1069
  editableTitle: {
1069
1070
  untitled: "Untitled Clip (已本地化)",
@@ -1055,6 +1055,7 @@ const messages = {
1055
1055
  "瀏覽器記錄選項已就緒,正在等待 Chrome Web Store URL。",
1056
1056
  desktopTitle: "桌面應用程式",
1057
1057
  desktopDescription: "最適合全域快捷鍵、選單列錄製、會議與重複擷取。",
1058
+ openDesktopApp: "開啟桌面應用程式",
1058
1059
  },
1059
1060
  editableTitle: {
1060
1061
  untitled: "未命名剪輯",