blume 1.6.0 → 1.6.1

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 (83) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cli/index.js +434 -150
  3. package/dist/cli/index.js.map +59 -56
  4. package/dist/types/core/data.d.ts +10 -1
  5. package/dist/types/core/i18n-ui.d.ts +2 -0
  6. package/dist/types/core/schema.d.ts +5 -0
  7. package/dist/types/core/types.d.ts +6 -0
  8. package/dist/types/openapi/references.d.ts +5 -0
  9. package/docs/07-faq.mdx +9 -9
  10. package/docs/advanced/api-reference.mdx +10 -1
  11. package/docs/advanced/custom-pages.mdx +3 -1
  12. package/docs/advanced/graphql.mdx +1 -1
  13. package/docs/configuration/ai.mdx +4 -0
  14. package/docs/configuration/seo.mdx +3 -3
  15. package/docs/configuration/theming.mdx +6 -0
  16. package/docs/content/components.mdx +8 -1
  17. package/package.json +53 -53
  18. package/src/astro/examples.ts +29 -2
  19. package/src/astro/generate.ts +99 -61
  20. package/src/astro/index.ts +7 -0
  21. package/src/astro/markdown-negotiation.ts +1 -1
  22. package/src/astro/runtime-modules.ts +196 -0
  23. package/src/astro/templates.ts +241 -38
  24. package/src/cli/commands/build.ts +7 -1
  25. package/src/cli/commands/dev.ts +6 -3
  26. package/src/cli/host-args.ts +18 -0
  27. package/src/cli/index.ts +2 -1
  28. package/src/components/copy-feedback.ts +93 -9
  29. package/src/components/islands/ask-ai.tsx +4 -1
  30. package/src/components/islands/hooks.ts +3 -1
  31. package/src/components/layout/PageActions.astro +25 -14
  32. package/src/core/data.ts +10 -1
  33. package/src/core/define-components.ts +2 -0
  34. package/src/core/i18n-ui.ts +1 -0
  35. package/src/core/includes.ts +2 -1
  36. package/src/core/manifest.ts +10 -0
  37. package/src/core/schema.ts +13 -5
  38. package/src/core/types.ts +6 -0
  39. package/src/core/ui-packs/ar.ts +1 -0
  40. package/src/core/ui-packs/bg.ts +1 -0
  41. package/src/core/ui-packs/bn.ts +1 -0
  42. package/src/core/ui-packs/ca.ts +1 -0
  43. package/src/core/ui-packs/cs.ts +1 -0
  44. package/src/core/ui-packs/da.ts +1 -0
  45. package/src/core/ui-packs/de.ts +1 -0
  46. package/src/core/ui-packs/el.ts +1 -0
  47. package/src/core/ui-packs/es.ts +1 -0
  48. package/src/core/ui-packs/fa.ts +1 -0
  49. package/src/core/ui-packs/fi.ts +1 -0
  50. package/src/core/ui-packs/fr.ts +1 -0
  51. package/src/core/ui-packs/he.ts +1 -0
  52. package/src/core/ui-packs/hi.ts +1 -0
  53. package/src/core/ui-packs/hr.ts +1 -0
  54. package/src/core/ui-packs/hu.ts +1 -0
  55. package/src/core/ui-packs/id.ts +1 -0
  56. package/src/core/ui-packs/it.ts +1 -0
  57. package/src/core/ui-packs/ja.ts +1 -0
  58. package/src/core/ui-packs/ko.ts +1 -0
  59. package/src/core/ui-packs/nl.ts +1 -0
  60. package/src/core/ui-packs/no.ts +1 -0
  61. package/src/core/ui-packs/pl.ts +1 -0
  62. package/src/core/ui-packs/pt-br.ts +1 -0
  63. package/src/core/ui-packs/pt.ts +1 -0
  64. package/src/core/ui-packs/ro.ts +1 -0
  65. package/src/core/ui-packs/ru.ts +1 -0
  66. package/src/core/ui-packs/sk.ts +1 -0
  67. package/src/core/ui-packs/sr.ts +1 -0
  68. package/src/core/ui-packs/sv.ts +1 -0
  69. package/src/core/ui-packs/th.ts +1 -0
  70. package/src/core/ui-packs/tr.ts +1 -0
  71. package/src/core/ui-packs/uk.ts +1 -0
  72. package/src/core/ui-packs/vi.ts +1 -0
  73. package/src/core/ui-packs/zh-tw.ts +1 -0
  74. package/src/core/ui-packs/zh.ts +1 -0
  75. package/src/core/version-cut.ts +5 -3
  76. package/src/deploy/vercel-negotiation.ts +49 -6
  77. package/src/og/card.ts +1 -1
  78. package/src/openapi/references.ts +8 -0
  79. package/src/openapi/render-mdx.ts +18 -4
  80. package/src/openapi/scalar.ts +0 -4
  81. package/src/registry/eject.ts +36 -17
  82. package/src/theme/entry.ts +2 -2
  83. package/src/theme/sources.ts +49 -0
@@ -176,7 +176,7 @@ const AskAI = ({
176
176
  useEffect(() => {
177
177
  // The initial null→body flip is deliberate (there is no body during SSR);
178
178
  // it is the same one-time post-mount cascade the old `mounted` flag had.
179
- // oxlint-disable-next-line react/react-compiler -- deliberate post-mount portal-target initialization
179
+ // oxlint-disable-next-line react/react-compiler, react/set-state-in-effect -- deliberate post-mount portal-target initialization
180
180
  setPortalTarget(document.body);
181
181
  const onSwap = () => setPortalTarget(document.body);
182
182
  document.addEventListener("astro:after-swap", onSwap);
@@ -263,6 +263,7 @@ const AskAI = ({
263
263
  if (open && portalTarget) {
264
264
  document.body.dataset.blumeAsk = "open";
265
265
  }
266
+ // oxlint-disable-next-line react/exhaustive-effect-dependencies -- see above
266
267
  }, [open, portalTarget]);
267
268
 
268
269
  // Below the desktop dock breakpoint the open panel is a full-width overlay,
@@ -328,12 +329,14 @@ const AskAI = ({
328
329
  // portalTarget: each swap installs a new <body>, so the sweep and its
329
330
  // observer must re-run against the new children (the old ones are
330
331
  // detached).
332
+ // oxlint-disable-next-line react/exhaustive-effect-dependencies -- see above
331
333
  }, [open, portalTarget]);
332
334
 
333
335
  // Keep the newest message in view as it streams in — and after a swap, when
334
336
  // the re-portaled panel's scroll container is reborn at the top.
335
337
  useEffect(() => {
336
338
  scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight });
339
+ // oxlint-disable-next-line react/exhaustive-effect-dependencies -- both deps are triggers, not values read here: re-run per streamed message and per re-portaled panel
337
340
  }, [messages, portalTarget]);
338
341
 
339
342
  const runQuestion = (raw: string) => {
@@ -57,7 +57,7 @@ const useClientData = (): BlumeClientData | null => {
57
57
  // Intentional post-mount hydration guard: `null` on the server and first
58
58
  // client render so hydration matches, then the snapshot once mounted. The
59
59
  // extra render is required; do not seed the initial value from the DOM.
60
- // oxlint-disable-next-line react/react-compiler, react-doctor/no-initialize-state -- deliberate SSR hydration guard
60
+ // oxlint-disable-next-line react/react-compiler, react/set-state-in-effect, react-doctor/no-initialize-state -- deliberate SSR hydration guard
61
61
  useEffect(() => setData(readClientData()), []);
62
62
  return data;
63
63
  };
@@ -109,6 +109,7 @@ export const useSearch = (): UseSearch => {
109
109
  // Before the lazy client import: the first search's heaviest phase is
110
110
  // creating the provider client (index download), and it must show loading.
111
111
  setLoading(true);
112
+ // oxlint-disable-next-line react/todo -- React Compiler cannot lower try/finally; the hook stays manually memoized above
112
113
  try {
113
114
  if (!searchFn.current) {
114
115
  const { createSearch } = await import("blume:search-client");
@@ -250,6 +251,7 @@ export const useAskAI = (options: UseAskAIOptions = {}): UseAskAI => {
250
251
  assistant.content = errorMessage;
251
252
  setMessages([...history, { ...assistant }]);
252
253
  }
254
+ // oxlint-disable-next-line react/todo -- React Compiler cannot lower try/finally; the hook stays manually memoized above
253
255
  } finally {
254
256
  if (live()) {
255
257
  setLoading(false);
@@ -104,6 +104,7 @@ const menuRowClass =
104
104
  data-blume-page-actions
105
105
  data-i18n-chat-prompt={a.openInChatPrompt}
106
106
  data-i18n-copied={a.copied}
107
+ data-i18n-copy-failed={a.copyFailed}
107
108
  data-i18n-generating={a.generating}
108
109
  data-mcp-name={mcpName ?? undefined}
109
110
  data-mcp-url={mcpUrl ?? undefined}
@@ -259,7 +260,7 @@ const menuRowClass =
259
260
  </div>
260
261
 
261
262
  <script>
262
- import { copyText, flashLabel } from "../copy-feedback.ts";
263
+ import { copyDeferredText, copyText, flashLabel } from "../copy-feedback.ts";
263
264
  import { installDropdownDismiss } from "../dropdown-dismiss.ts";
264
265
  import { prefixBase } from "../islands/base-path.ts";
265
266
  import { rafThrottle } from "../raf-throttle.ts";
@@ -393,6 +394,8 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
393
394
  const mdRoute = root.getAttribute("data-md") ?? "";
394
395
  const md = prefixBase(import.meta.env.BASE_URL, mdRoute);
395
396
  const copiedLabel = root.getAttribute("data-i18n-copied") || "Copied!";
397
+ const copyFailedLabel =
398
+ root.getAttribute("data-i18n-copy-failed") || "Copy failed";
396
399
  const generatingLabel =
397
400
  root.getAttribute("data-i18n-generating") || "Generating…";
398
401
  // Always stamped: the frontmatter merges `EN_UI.actions` under any
@@ -444,8 +447,9 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
444
447
  );
445
448
 
446
449
  const copy = async (value: string, el: Element | null) => {
447
- if ((await copyText(value)) && el instanceof HTMLElement) {
448
- flashLabel(el, copiedLabel);
450
+ const copied = await copyText(value);
451
+ if (el instanceof HTMLElement) {
452
+ flashLabel(el, copied ? copiedLabel : copyFailedLabel);
449
453
  }
450
454
  };
451
455
  root
@@ -475,21 +479,28 @@ hr { border: 0; border-top: 1px solid #ddd; margin: 2em 0; }`;
475
479
  root
476
480
  .querySelector("[data-blume-copy-page]")
477
481
  ?.addEventListener("click", async () => {
482
+ let copied = false;
478
483
  try {
479
- const response = await fetch(md);
480
- if (!response.ok) {
481
- throw new Error(`Fetching ${md} failed (${response.status})`);
482
- }
483
- if (
484
- (await copyText(await response.text())) &&
485
- label instanceof HTMLElement
486
- ) {
487
- flashLabel(label, copiedLabel);
488
- }
484
+ // The Markdown is fetched on click; the deferred write keeps the
485
+ // clipboard call inside the click's user activation (Safari and
486
+ // Firefox reject a write issued after an `await`).
487
+ copied = await copyDeferredText(async () => {
488
+ const response = await fetch(md);
489
+ if (!response.ok) {
490
+ throw new Error(`Fetching ${md} failed (${response.status})`);
491
+ }
492
+ return response.text();
493
+ });
489
494
  } catch (error) {
490
- // Clipboard or fetch unavailable; don't flash "Copied!" untruthfully.
491
495
  console.error("[blume] Copy as Markdown failed", error);
492
496
  }
497
+ // Never flash "Copied!" untruthfully — but never stay silent either:
498
+ // a reader in an in-app browser or on a denied clipboard prompt must
499
+ // see that nothing landed on the clipboard. The legacy copy command
500
+ // has already been tried by the time the helper reports `false`.
501
+ if (label instanceof HTMLElement) {
502
+ flashLabel(label, copied ? copiedLabel : copyFailedLabel);
503
+ }
493
504
  });
494
505
 
495
506
  root
package/src/core/data.ts CHANGED
@@ -78,6 +78,11 @@ export interface BlumeRoute {
78
78
  alternates: RouteAlternate[];
79
79
  /** Astro collection the entry renders through (`"docs"` | `"staged"`). */
80
80
  collection: string;
81
+ /**
82
+ * Meta description as the page head renders it (`seo.description` over the
83
+ * front matter `description`), or `null` when the page declares neither.
84
+ */
85
+ description: string | null;
81
86
  draft: boolean;
82
87
  /** "Edit this page" URL, or `null` when no repo/source provides one. */
83
88
  editUrl: string | null;
@@ -161,7 +166,11 @@ export interface BlumeDataConfig {
161
166
  * kept out of this snapshot, which pages serialize into HTML.
162
167
  */
163
168
  og: {
164
- /** Card subtitle: `seo.og.description` (`false` omits it) over the site description. */
169
+ /**
170
+ * Site-wide card subtitle: `seo.og.description` (`false` omits it) over
171
+ * the site description. A page with its own description shows that
172
+ * instead; this is the fallback for pages without one.
173
+ */
165
174
  description?: string;
166
175
  enabled: boolean;
167
176
  /** Inlined SVG brand mark; `false` renders the card without any mark. */
@@ -4,6 +4,7 @@ import type { HydrationMode } from "./schema.ts";
4
4
  * A reference to a component. Either an imported component (preferred, for type
5
5
  * safety) or a string path resolved relative to the project root.
6
6
  */
7
+ // oxlint-disable-next-line anti-slop/no-unknown-type-aliases -- deliberately untyped: user configs pass imported components from any framework (React functions, Svelte classes, Vue SFC objects), which share no structural type
7
8
  export type ComponentReference = unknown | string;
8
9
 
9
10
  /** An interactive component plus its hydration strategy. */
@@ -15,6 +16,7 @@ export interface IslandDescriptor {
15
16
  }
16
17
 
17
18
  /** A component override: a static component or a hydrated island. */
19
+ // oxlint-disable-next-line anti-slop/no-unknown-type-aliases -- inherits the untyped `ComponentReference` above
18
20
  export type ComponentOverride = ComponentReference | IslandDescriptor;
19
21
 
20
22
  /** User-authored component overrides, grouped by surface. */
@@ -21,6 +21,7 @@ const uiStringsObject = z.object({
21
21
  copyClaudeCode: z.string().default("Copy Claude Code command"),
22
22
  copyCode: z.string().default("Copy code"),
23
23
  copyCodex: z.string().default("Copy Codex command"),
24
+ copyFailed: z.string().default("Copy failed"),
24
25
  copyMarkdown: z.string().default("Copy as Markdown"),
25
26
  copyServerUrl: z.string().default("Copy server URL"),
26
27
  edit: z.string().default("Edit on GitHub"),
@@ -622,7 +622,8 @@ export const buildIncludeGraph = (
622
622
  continue;
623
623
  }
624
624
  for (const partial of page.includes ?? []) {
625
- const includers = (graph[partial] ??= []);
625
+ const includers = graph[partial] ?? [];
626
+ graph[partial] = includers;
626
627
  if (!includers.includes(sourcePath)) {
627
628
  includers.push(sourcePath);
628
629
  }
@@ -63,6 +63,14 @@ export const contentIndexable = (
63
63
  * output is fully prerendered (render-fallback, no client redirect). Fallback
64
64
  * routes are not indexed and carry no `hreflang` of their own.
65
65
  */
66
+ /**
67
+ * The description a page's `<head>` emits — `seo.description` over the front
68
+ * matter `description` — mirrored onto its route so the OG card's subtitle
69
+ * matches the page's `og:description`.
70
+ */
71
+ const routeDescription = (page: PageRecord): string | undefined =>
72
+ page.meta.seo.description ?? page.description;
73
+
66
74
  const buildFallbackRoutes = (
67
75
  graph: ContentGraph,
68
76
  i18n: NonNullable<ResolvedConfig["i18n"]>,
@@ -108,6 +116,7 @@ const buildFallbackRoutes = (
108
116
  alternates: alternatesByKey.get(key) ?? [],
109
117
  collection: source.collection ?? "docs",
110
118
  contentType: source.contentType,
119
+ description: routeDescription(source),
111
120
  draft: source.meta.draft,
112
121
  editUrl: source.editUrl,
113
122
  entryId: source.entryId ?? source.source.ref,
@@ -171,6 +180,7 @@ export const buildManifest = (options: {
171
180
  alternates: alternatesByKey.get(page.translationKey) ?? [],
172
181
  collection: page.collection ?? "docs",
173
182
  contentType: page.contentType,
183
+ description: routeDescription(page),
174
184
  draft: page.meta.draft,
175
185
  editUrl: page.editUrl,
176
186
  entryId: page.entryId ?? page.source.ref,
@@ -907,9 +907,8 @@ const aiConfigSchema = z.strictObject({
907
907
  llmsTxt: z
908
908
  .union([z.boolean(), llmsTxtObjectSchema])
909
909
  .default(true)
910
- .transform(
911
- (value): LlmsTxtResolved =>
912
- isBoolean(value) ? { enabled: value, openapi: true } : value
910
+ .transform((value): LlmsTxtResolved =>
911
+ isBoolean(value) ? { enabled: value, openapi: true } : value
913
912
  ),
914
913
  // Serializers for the agent-facing Markdown downlevel (the `.md` mirror,
915
914
  // llms-full.txt, MCP get_page), keyed by JSX name. Functions live here —
@@ -1332,8 +1331,9 @@ const ogFontSchema = z.union([
1332
1331
 
1333
1332
  const ogConfigSchema = z.strictObject({
1334
1333
  /**
1335
- * Card subtitle. Defaults to the site description; a string overrides it,
1336
- * `false` renders the card without one.
1334
+ * Site-wide card subtitle, shown on pages without their own description.
1335
+ * Defaults to the site description; a string overrides it, `false` renders
1336
+ * every card without a subtitle (page descriptions included).
1337
1337
  */
1338
1338
  description: z.union([z.string(), z.literal(false)]).optional(),
1339
1339
  /**
@@ -1708,6 +1708,14 @@ const openapiSourceSchema = z.strictObject({
1708
1708
  noindex: z.boolean().default(false),
1709
1709
  /** Per-source route; defaults to the block's `route` (or a derived path). */
1710
1710
  route: z.string().optional(),
1711
+ /**
1712
+ * Append the English "Reference for the … endpoint in the … API." sentence
1713
+ * to every generated operation page's meta description. On by default, so
1714
+ * terse specs still ship distinct, snippet-length descriptions; set to
1715
+ * `false` on a non-English site to describe pages with the spec's own prose
1716
+ * alone (falling back to the page title when an operation has none).
1717
+ */
1718
+ seoDescriptionSuffix: z.boolean().default(true),
1711
1719
  /** Local path or `http(s)` URL to the spec. */
1712
1720
  spec: z.string(),
1713
1721
  });
package/src/core/types.ts CHANGED
@@ -368,6 +368,12 @@ export interface RouteManifestEntry {
368
368
  /** Adapter-supplied "edit this page" URL (non-filesystem sources). */
369
369
  editUrl?: string;
370
370
  title: string;
371
+ /**
372
+ * The page's meta description as the head renders it: `seo.description`
373
+ * over the front matter `description`. Feeds the generated OG card's
374
+ * subtitle, so a shared link's image and its `og:description` agree.
375
+ */
376
+ description?: string;
371
377
  contentType: string;
372
378
  hidden: boolean;
373
379
  draft: boolean;
@@ -11,6 +11,7 @@ export const ar: UIStringsOverride = {
11
11
  copyClaudeCode: "نسخ أمر Claude Code",
12
12
  copyCode: "نسخ الكود",
13
13
  copyCodex: "نسخ أمر Codex",
14
+ copyFailed: "فشل النسخ",
14
15
  copyMarkdown: "نسخ بصيغة Markdown",
15
16
  copyServerUrl: "نسخ عنوان URL للخادم",
16
17
  edit: "التعديل على GitHub",
@@ -11,6 +11,7 @@ export const bg: UIStringsOverride = {
11
11
  copyClaudeCode: "Копирай командата на Claude Code",
12
12
  copyCode: "Копирай кода",
13
13
  copyCodex: "Копирай командата на Codex",
14
+ copyFailed: "Копирането не бе успешно",
14
15
  copyMarkdown: "Копирай като Markdown",
15
16
  copyServerUrl: "Копирай URL на сървъра",
16
17
  edit: "Редактирай в GitHub",
@@ -11,6 +11,7 @@ export const bn: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code কমান্ড অনুলিপি করুন",
12
12
  copyCode: "কোড অনুলিপি করুন",
13
13
  copyCodex: "Codex কমান্ড অনুলিপি করুন",
14
+ copyFailed: "অনুলিপি ব্যর্থ হয়েছে",
14
15
  copyMarkdown: "Markdown হিসেবে অনুলিপি করুন",
15
16
  copyServerUrl: "সার্ভার URL অনুলিপি করুন",
16
17
  edit: "GitHub-এ সম্পাদনা করুন",
@@ -11,6 +11,7 @@ export const ca: UIStringsOverride = {
11
11
  copyClaudeCode: "Copia l'ordre de Claude Code",
12
12
  copyCode: "Copia el codi",
13
13
  copyCodex: "Copia l'ordre de Codex",
14
+ copyFailed: "No s'ha pogut copiar",
14
15
  copyMarkdown: "Copia com a Markdown",
15
16
  copyServerUrl: "Copia l'URL del servidor",
16
17
  edit: "Edita a GitHub",
@@ -11,6 +11,7 @@ export const cs: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopírovat příkaz Claude Code",
12
12
  copyCode: "Kopírovat kód",
13
13
  copyCodex: "Kopírovat příkaz Codex",
14
+ copyFailed: "Kopírování se nezdařilo",
14
15
  copyMarkdown: "Kopírovat jako Markdown",
15
16
  copyServerUrl: "Kopírovat URL serveru",
16
17
  edit: "Upravit na GitHubu",
@@ -11,6 +11,7 @@ export const da: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopiér Claude Code-kommando",
12
12
  copyCode: "Kopiér kode",
13
13
  copyCodex: "Kopiér Codex-kommando",
14
+ copyFailed: "Kopiering mislykkedes",
14
15
  copyMarkdown: "Kopiér som Markdown",
15
16
  copyServerUrl: "Kopiér server-URL",
16
17
  edit: "Rediger på GitHub",
@@ -11,6 +11,7 @@ export const de: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude-Code-Befehl kopieren",
12
12
  copyCode: "Code kopieren",
13
13
  copyCodex: "Codex-Befehl kopieren",
14
+ copyFailed: "Kopieren fehlgeschlagen",
14
15
  copyMarkdown: "Als Markdown kopieren",
15
16
  copyServerUrl: "Server-URL kopieren",
16
17
  edit: "Auf GitHub bearbeiten",
@@ -11,6 +11,7 @@ export const el: UIStringsOverride = {
11
11
  copyClaudeCode: "Αντιγραφή εντολής Claude Code",
12
12
  copyCode: "Αντιγραφή κώδικα",
13
13
  copyCodex: "Αντιγραφή εντολής Codex",
14
+ copyFailed: "Η αντιγραφή απέτυχε",
14
15
  copyMarkdown: "Αντιγραφή ως Markdown",
15
16
  copyServerUrl: "Αντιγραφή URL διακομιστή",
16
17
  edit: "Επεξεργασία στο GitHub",
@@ -11,6 +11,7 @@ export const es: UIStringsOverride = {
11
11
  copyClaudeCode: "Copiar comando de Claude Code",
12
12
  copyCode: "Copiar código",
13
13
  copyCodex: "Copiar comando de Codex",
14
+ copyFailed: "Error al copiar",
14
15
  copyMarkdown: "Copiar como Markdown",
15
16
  copyServerUrl: "Copiar URL del servidor",
16
17
  edit: "Editar en GitHub",
@@ -11,6 +11,7 @@ export const fa: UIStringsOverride = {
11
11
  copyClaudeCode: "کپی دستور Claude Code",
12
12
  copyCode: "کپی کد",
13
13
  copyCodex: "کپی دستور Codex",
14
+ copyFailed: "کپی ناموفق بود",
14
15
  copyMarkdown: "کپی به‌صورت Markdown",
15
16
  copyServerUrl: "کپی نشانی سرور",
16
17
  edit: "ویرایش در GitHub",
@@ -11,6 +11,7 @@ export const fi: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopioi Claude Code -komento",
12
12
  copyCode: "Kopioi koodi",
13
13
  copyCodex: "Kopioi Codex-komento",
14
+ copyFailed: "Kopiointi epäonnistui",
14
15
  copyMarkdown: "Kopioi Markdownina",
15
16
  copyServerUrl: "Kopioi palvelimen URL",
16
17
  edit: "Muokkaa GitHubissa",
@@ -11,6 +11,7 @@ export const fr: UIStringsOverride = {
11
11
  copyClaudeCode: "Copier la commande Claude Code",
12
12
  copyCode: "Copier le code",
13
13
  copyCodex: "Copier la commande Codex",
14
+ copyFailed: "Échec de la copie",
14
15
  copyMarkdown: "Copier en Markdown",
15
16
  copyServerUrl: "Copier l'URL du serveur",
16
17
  edit: "Modifier sur GitHub",
@@ -11,6 +11,7 @@ export const he: UIStringsOverride = {
11
11
  copyClaudeCode: "העתק פקודת Claude Code",
12
12
  copyCode: "העתק קוד",
13
13
  copyCodex: "העתק פקודת Codex",
14
+ copyFailed: "ההעתקה נכשלה",
14
15
  copyMarkdown: "העתק כ-Markdown",
15
16
  copyServerUrl: "העתק כתובת URL של השרת",
16
17
  edit: "ערוך ב-GitHub",
@@ -11,6 +11,7 @@ export const hi: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code कमांड कॉपी करें",
12
12
  copyCode: "कोड कॉपी करें",
13
13
  copyCodex: "Codex कमांड कॉपी करें",
14
+ copyFailed: "कॉपी विफल रहा",
14
15
  copyMarkdown: "Markdown के रूप में कॉपी करें",
15
16
  copyServerUrl: "सर्वर URL कॉपी करें",
16
17
  edit: "GitHub पर संपादित करें",
@@ -11,6 +11,7 @@ export const hr: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopiraj naredbu Claude Code",
12
12
  copyCode: "Kopiraj kôd",
13
13
  copyCodex: "Kopiraj naredbu Codex",
14
+ copyFailed: "Kopiranje nije uspjelo",
14
15
  copyMarkdown: "Kopiraj kao Markdown",
15
16
  copyServerUrl: "Kopiraj URL poslužitelja",
16
17
  edit: "Uredi na GitHubu",
@@ -11,6 +11,7 @@ export const hu: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code parancs másolása",
12
12
  copyCode: "Kód másolása",
13
13
  copyCodex: "Codex parancs másolása",
14
+ copyFailed: "A másolás nem sikerült",
14
15
  copyMarkdown: "Másolás Markdownként",
15
16
  copyServerUrl: "Szerver URL másolása",
16
17
  edit: "Szerkesztés a GitHubon",
@@ -11,6 +11,7 @@ export const id: UIStringsOverride = {
11
11
  copyClaudeCode: "Salin perintah Claude Code",
12
12
  copyCode: "Salin kode",
13
13
  copyCodex: "Salin perintah Codex",
14
+ copyFailed: "Gagal menyalin",
14
15
  copyMarkdown: "Salin sebagai Markdown",
15
16
  copyServerUrl: "Salin URL server",
16
17
  edit: "Edit di GitHub",
@@ -11,6 +11,7 @@ export const it: UIStringsOverride = {
11
11
  copyClaudeCode: "Copia comando di Claude Code",
12
12
  copyCode: "Copia codice",
13
13
  copyCodex: "Copia comando di Codex",
14
+ copyFailed: "Copia non riuscita",
14
15
  copyMarkdown: "Copia come Markdown",
15
16
  copyServerUrl: "Copia URL del server",
16
17
  edit: "Modifica su GitHub",
@@ -11,6 +11,7 @@ export const ja: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code コマンドをコピー",
12
12
  copyCode: "コードをコピー",
13
13
  copyCodex: "Codex コマンドをコピー",
14
+ copyFailed: "コピーに失敗しました",
14
15
  copyMarkdown: "Markdown としてコピー",
15
16
  copyServerUrl: "サーバー URL をコピー",
16
17
  edit: "GitHub で編集",
@@ -11,6 +11,7 @@ export const ko: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code 명령 복사",
12
12
  copyCode: "코드 복사",
13
13
  copyCodex: "Codex 명령 복사",
14
+ copyFailed: "복사 실패",
14
15
  copyMarkdown: "Markdown으로 복사",
15
16
  copyServerUrl: "서버 URL 복사",
16
17
  edit: "GitHub에서 편집",
@@ -11,6 +11,7 @@ export const nl: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code-opdracht kopiëren",
12
12
  copyCode: "Code kopiëren",
13
13
  copyCodex: "Codex-opdracht kopiëren",
14
+ copyFailed: "Kopiëren mislukt",
14
15
  copyMarkdown: "Kopiëren als Markdown",
15
16
  copyServerUrl: "Server-URL kopiëren",
16
17
  edit: "Bewerken op GitHub",
@@ -11,6 +11,7 @@ export const no: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopier Claude Code-kommando",
12
12
  copyCode: "Kopier kode",
13
13
  copyCodex: "Kopier Codex-kommando",
14
+ copyFailed: "Kopiering mislyktes",
14
15
  copyMarkdown: "Kopier som Markdown",
15
16
  copyServerUrl: "Kopier server-URL",
16
17
  edit: "Rediger på GitHub",
@@ -11,6 +11,7 @@ export const pl: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopiuj polecenie Claude Code",
12
12
  copyCode: "Kopiuj kod",
13
13
  copyCodex: "Kopiuj polecenie Codex",
14
+ copyFailed: "Kopiowanie nie powiodło się",
14
15
  copyMarkdown: "Kopiuj jako Markdown",
15
16
  copyServerUrl: "Kopiuj adres URL serwera",
16
17
  edit: "Edytuj na GitHubie",
@@ -11,6 +11,7 @@ export const ptBR: UIStringsOverride = {
11
11
  copyClaudeCode: "Copiar comando do Claude Code",
12
12
  copyCode: "Copiar código",
13
13
  copyCodex: "Copiar comando do Codex",
14
+ copyFailed: "Falha ao copiar",
14
15
  copyMarkdown: "Copiar como Markdown",
15
16
  copyServerUrl: "Copiar URL do servidor",
16
17
  edit: "Editar no GitHub",
@@ -11,6 +11,7 @@ export const pt: UIStringsOverride = {
11
11
  copyClaudeCode: "Copiar comando do Claude Code",
12
12
  copyCode: "Copiar código",
13
13
  copyCodex: "Copiar comando do Codex",
14
+ copyFailed: "Falha ao copiar",
14
15
  copyMarkdown: "Copiar como Markdown",
15
16
  copyServerUrl: "Copiar URL do servidor",
16
17
  edit: "Editar no GitHub",
@@ -11,6 +11,7 @@ export const ro: UIStringsOverride = {
11
11
  copyClaudeCode: "Copiază comanda Claude Code",
12
12
  copyCode: "Copiază codul",
13
13
  copyCodex: "Copiază comanda Codex",
14
+ copyFailed: "Copierea a eșuat",
14
15
  copyMarkdown: "Copiază ca Markdown",
15
16
  copyServerUrl: "Copiază URL-ul serverului",
16
17
  edit: "Editează pe GitHub",
@@ -11,6 +11,7 @@ export const ru: UIStringsOverride = {
11
11
  copyClaudeCode: "Скопировать команду Claude Code",
12
12
  copyCode: "Скопировать код",
13
13
  copyCodex: "Скопировать команду Codex",
14
+ copyFailed: "Не удалось скопировать",
14
15
  copyMarkdown: "Скопировать как Markdown",
15
16
  copyServerUrl: "Скопировать URL сервера",
16
17
  edit: "Редактировать на GitHub",
@@ -11,6 +11,7 @@ export const sk: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopírovať príkaz Claude Code",
12
12
  copyCode: "Kopírovať kód",
13
13
  copyCodex: "Kopírovať príkaz Codex",
14
+ copyFailed: "Kopírovanie zlyhalo",
14
15
  copyMarkdown: "Kopírovať ako Markdown",
15
16
  copyServerUrl: "Kopírovať URL servera",
16
17
  edit: "Upraviť na GitHube",
@@ -11,6 +11,7 @@ export const sr: UIStringsOverride = {
11
11
  copyClaudeCode: "Копирај Claude Code команду",
12
12
  copyCode: "Копирај код",
13
13
  copyCodex: "Копирај Codex команду",
14
+ copyFailed: "Копирање није успело",
14
15
  copyMarkdown: "Копирај као Markdown",
15
16
  copyServerUrl: "Копирај URL сервера",
16
17
  edit: "Уреди на GitHub-у",
@@ -11,6 +11,7 @@ export const sv: UIStringsOverride = {
11
11
  copyClaudeCode: "Kopiera Claude Code-kommando",
12
12
  copyCode: "Kopiera kod",
13
13
  copyCodex: "Kopiera Codex-kommando",
14
+ copyFailed: "Kopiering misslyckades",
14
15
  copyMarkdown: "Kopiera som Markdown",
15
16
  copyServerUrl: "Kopiera server-URL",
16
17
  edit: "Redigera på GitHub",
@@ -11,6 +11,7 @@ export const th: UIStringsOverride = {
11
11
  copyClaudeCode: "คัดลอกคำสั่ง Claude Code",
12
12
  copyCode: "คัดลอกโค้ด",
13
13
  copyCodex: "คัดลอกคำสั่ง Codex",
14
+ copyFailed: "คัดลอกไม่สำเร็จ",
14
15
  copyMarkdown: "คัดลอกเป็น Markdown",
15
16
  copyServerUrl: "คัดลอก URL ของเซิร์ฟเวอร์",
16
17
  edit: "แก้ไขบน GitHub",
@@ -11,6 +11,7 @@ export const tr: UIStringsOverride = {
11
11
  copyClaudeCode: "Claude Code komutunu kopyala",
12
12
  copyCode: "Kodu kopyala",
13
13
  copyCodex: "Codex komutunu kopyala",
14
+ copyFailed: "Kopyalama başarısız oldu",
14
15
  copyMarkdown: "Markdown olarak kopyala",
15
16
  copyServerUrl: "Sunucu URL'sini kopyala",
16
17
  edit: "GitHub'da düzenle",
@@ -11,6 +11,7 @@ export const uk: UIStringsOverride = {
11
11
  copyClaudeCode: "Скопіювати команду Claude Code",
12
12
  copyCode: "Скопіювати код",
13
13
  copyCodex: "Скопіювати команду Codex",
14
+ copyFailed: "Не вдалося скопіювати",
14
15
  copyMarkdown: "Скопіювати як Markdown",
15
16
  copyServerUrl: "Скопіювати URL сервера",
16
17
  edit: "Редагувати на GitHub",
@@ -11,6 +11,7 @@ export const vi: UIStringsOverride = {
11
11
  copyClaudeCode: "Sao chép lệnh Claude Code",
12
12
  copyCode: "Sao chép mã",
13
13
  copyCodex: "Sao chép lệnh Codex",
14
+ copyFailed: "Sao chép thất bại",
14
15
  copyMarkdown: "Sao chép dưới dạng Markdown",
15
16
  copyServerUrl: "Sao chép URL máy chủ",
16
17
  edit: "Chỉnh sửa trên GitHub",
@@ -11,6 +11,7 @@ export const zhTW: UIStringsOverride = {
11
11
  copyClaudeCode: "複製 Claude Code 指令",
12
12
  copyCode: "複製程式碼",
13
13
  copyCodex: "複製 Codex 指令",
14
+ copyFailed: "複製失敗",
14
15
  copyMarkdown: "複製為 Markdown",
15
16
  copyServerUrl: "複製伺服器 URL",
16
17
  edit: "在 GitHub 上編輯",
@@ -11,6 +11,7 @@ export const zh: UIStringsOverride = {
11
11
  copyClaudeCode: "复制 Claude Code 命令",
12
12
  copyCode: "复制代码",
13
13
  copyCodex: "复制 Codex 命令",
14
+ copyFailed: "复制失败",
14
15
  copyMarkdown: "复制为 Markdown",
15
16
  copyServerUrl: "复制服务器 URL",
16
17
  edit: "在 GitHub 上编辑",