@emdash-cms/admin 0.9.0 → 0.10.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.
Files changed (87) hide show
  1. package/dist/{LocaleDirectionProvider-BGcmv_N7.js → LocaleDirectionProvider-xcSZTLFA.js} +22 -22
  2. package/dist/LocaleDirectionProvider-xcSZTLFA.js.map +1 -0
  3. package/dist/{config-YdN-uv6S.d.ts → config-DJUFU9nY.d.ts} +3 -3
  4. package/dist/{config-YdN-uv6S.d.ts.map → config-DJUFU9nY.d.ts.map} +1 -1
  5. package/dist/index.d.ts +123 -35
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +1560 -969
  8. package/dist/index.js.map +1 -1
  9. package/dist/locales/ar/messages.mjs +1 -1
  10. package/dist/locales/de/messages.mjs +1 -1
  11. package/dist/locales/en/messages.mjs +1 -1
  12. package/dist/locales/es-419/messages.mjs +1 -1
  13. package/dist/locales/eu/messages.mjs +1 -1
  14. package/dist/locales/fa/messages.mjs +1 -1
  15. package/dist/locales/fr/messages.mjs +1 -1
  16. package/dist/locales/id/messages.mjs +1 -1
  17. package/dist/locales/index.d.ts +1 -1
  18. package/dist/locales/index.js +1 -1
  19. package/dist/locales/ja/messages.mjs +1 -1
  20. package/dist/locales/ko/messages.mjs +1 -1
  21. package/dist/locales/pl/messages.mjs +1 -1
  22. package/dist/locales/pseudo/messages.mjs +1 -1
  23. package/dist/locales/pt-BR/messages.mjs +1 -1
  24. package/dist/locales/zh-CN/messages.mjs +1 -1
  25. package/dist/locales/zh-TW/messages.mjs +1 -1
  26. package/dist/messages-BLHOmJoS.js +6 -0
  27. package/dist/messages-BLHOmJoS.js.map +1 -0
  28. package/dist/messages-BLPEG0Mx.js +6 -0
  29. package/dist/messages-BLPEG0Mx.js.map +1 -0
  30. package/dist/messages-BYyq7qnh.js +6 -0
  31. package/dist/messages-BYyq7qnh.js.map +1 -0
  32. package/dist/messages-BcI5YTGJ.js +6 -0
  33. package/dist/messages-BcI5YTGJ.js.map +1 -0
  34. package/dist/messages-BpVvs7xa.js +6 -0
  35. package/dist/messages-BpVvs7xa.js.map +1 -0
  36. package/dist/messages-CSs_iEr-.js +6 -0
  37. package/dist/messages-CSs_iEr-.js.map +1 -0
  38. package/dist/messages-CvhBZHPh.js +6 -0
  39. package/dist/messages-CvhBZHPh.js.map +1 -0
  40. package/dist/messages-CyUGKznD.js +6 -0
  41. package/dist/messages-CyUGKznD.js.map +1 -0
  42. package/dist/messages-D7GebqCf.js +6 -0
  43. package/dist/messages-D7GebqCf.js.map +1 -0
  44. package/dist/messages-DDVTav-Y.js +6 -0
  45. package/dist/messages-DDVTav-Y.js.map +1 -0
  46. package/dist/messages-DMBcuYFC.js +6 -0
  47. package/dist/messages-DMBcuYFC.js.map +1 -0
  48. package/dist/messages-DRrZDx80.js +6 -0
  49. package/dist/messages-DRrZDx80.js.map +1 -0
  50. package/dist/messages-aWiCO6Ou.js +6 -0
  51. package/dist/messages-aWiCO6Ou.js.map +1 -0
  52. package/dist/messages-hcBnbmbA.js +6 -0
  53. package/dist/messages-hcBnbmbA.js.map +1 -0
  54. package/dist/messages-wnKLF74l.js +6 -0
  55. package/dist/messages-wnKLF74l.js.map +1 -0
  56. package/package.json +2 -2
  57. package/dist/LocaleDirectionProvider-BGcmv_N7.js.map +0 -1
  58. package/dist/messages-B2njiT3N.js +0 -6
  59. package/dist/messages-B2njiT3N.js.map +0 -1
  60. package/dist/messages-BH4d_eo3.js +0 -6
  61. package/dist/messages-BH4d_eo3.js.map +0 -1
  62. package/dist/messages-BI3LQfHj.js +0 -6
  63. package/dist/messages-BI3LQfHj.js.map +0 -1
  64. package/dist/messages-BLk7kRSt.js +0 -6
  65. package/dist/messages-BLk7kRSt.js.map +0 -1
  66. package/dist/messages-C0fFDufP.js +0 -6
  67. package/dist/messages-C0fFDufP.js.map +0 -1
  68. package/dist/messages-C2W1GOne.js +0 -6
  69. package/dist/messages-C2W1GOne.js.map +0 -1
  70. package/dist/messages-C7bn0sIO.js +0 -6
  71. package/dist/messages-C7bn0sIO.js.map +0 -1
  72. package/dist/messages-CKVC1F84.js +0 -6
  73. package/dist/messages-CKVC1F84.js.map +0 -1
  74. package/dist/messages-D6pXx3hr.js +0 -6
  75. package/dist/messages-D6pXx3hr.js.map +0 -1
  76. package/dist/messages-DQUgrzIz.js +0 -6
  77. package/dist/messages-DQUgrzIz.js.map +0 -1
  78. package/dist/messages-DfGxk-9q.js +0 -6
  79. package/dist/messages-DfGxk-9q.js.map +0 -1
  80. package/dist/messages-Dkb9TndV.js +0 -6
  81. package/dist/messages-Dkb9TndV.js.map +0 -1
  82. package/dist/messages-DzS9GmJY.js +0 -6
  83. package/dist/messages-DzS9GmJY.js.map +0 -1
  84. package/dist/messages-FaHuWwRM.js +0 -6
  85. package/dist/messages-FaHuWwRM.js.map +0 -1
  86. package/dist/messages-lrnW_v3N.js +0 -6
  87. package/dist/messages-lrnW_v3N.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,16 +1,16 @@
1
1
  import { c as fetchAuthMode, d as throwResponseError, i as fetchPlugins, l as fetchManifest, n as enablePlugin, o as API_BASE, r as fetchPlugin, s as apiFetch, t as disablePlugin, u as parseApiResponse } from "./plugins-DUJY_8HN.js";
2
- import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-BGcmv_N7.js";
2
+ import { a as SUPPORTED_LOCALES, i as DEFAULT_LOCALE, l as resolveLocale, n as useLocale, o as SUPPORTED_LOCALE_CODES, t as LocaleDirectionProvider } from "./LocaleDirectionProvider-xcSZTLFA.js";
3
3
  import "./locales/index.js";
4
4
  import { Badge, Button, Checkbox, CommandPalette, Dialog, Input, InputArea, Label, LinkButton, Loader, Popover, Select, Sidebar as KumoSidebar, Switch, Tabs, Toast, Toasty, Tooltip, buttonVariants, useSidebar } from "@cloudflare/kumo";
5
5
  import { i18n } from "@lingui/core";
6
6
  import { I18nProvider, Trans, useLingui } from "@lingui/react";
7
7
  import { QueryClient, QueryClientProvider, useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
8
8
  import { Link, Link as Link$1, Outlet, RouterProvider, createRootRouteWithContext, createRoute, createRouter, useLocation, useNavigate, useNavigate as useNavigate$1, useParams, useParams as useParams$1, useSearch } from "@tanstack/react-router";
9
- import * as React from "react";
10
- import { createContext, useCallback, useContext, useEffect, useState } from "react";
9
+ import * as React$1 from "react";
10
+ import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
11
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { Dialog as Dialog$1 } from "@cloudflare/kumo/primitives";
13
- import { ArrowCounterClockwise, ArrowRight, ArrowRightIcon, ArrowSquareOut, ArrowUUpLeft, ArrowUUpRight, ArrowsClockwise, ArrowsInSimple, ArrowsLeftRight, ArrowsOutSimple, BracketsCurly, Calendar, CalendarBlank, CaretDown, CaretRightIcon, CaretUp, CaretUpDown, ChatCircle, Check, CheckCircle, CircleDashed, CircleNotch, ClockCounterClockwise, Cloud, Code, CodeBlock, Copy, Cube, Database, DeviceMobile, DotsSixVertical, DownloadSimple, Envelope, EnvelopeSimple, Eye, EyeSlash, File, FileArrowDown, FileText, FloppyDisk, FolderOpen, Gear, GithubLogo, Globe, GlobeSimple, GridFour, HardDrive, Hash, Image as Image$1, ImageSquare, Info, Key, Link as Link$2, LinkBreak, LinkSimple, List, ListBullets, ListChecks, ListNumbers, MagnifyingGlass, Minus, Monitor, Moon, Palette, PaperPlaneTilt, Paragraph, Pencil, PencilSimple, PlugsConnected, Plus, Prohibit, PuzzlePiece, Quotes, Rows, Ruler, ShareNetwork, Shield, ShieldCheck, ShieldWarning, SignOut, SlidersHorizontal, Sparkle, SquaresFour, Stack, Storefront, Sun, TextAlignCenter, TextAlignLeft, TextAlignRight, TextB, TextHOne, TextHThree, TextHTwo, TextItalic, TextStrikethrough, TextT, TextUnderline, ToggleLeft, Trash, Upload, User, UserCircle, UserPlus, Users, Warning, WarningCircle, WebhooksLogo, X, YoutubeLogo } from "@phosphor-icons/react";
13
+ import { ArrowCounterClockwise, ArrowRight, ArrowRightIcon, ArrowSquareOut, ArrowUUpLeft, ArrowUUpRight, ArrowsClockwise, ArrowsInSimple, ArrowsLeftRight, ArrowsOutSimple, BracketsCurly, Calendar, CalendarBlank, CaretDown, CaretRightIcon, CaretUp, CaretUpDown, ChatCircle, Check, CheckCircle, CircleDashed, CircleNotch, ClockCounterClockwise, Cloud, Code, CodeBlock, Copy, Cube, Database, DeviceMobile, DotsSixVertical, DownloadSimple, Envelope, EnvelopeSimple, Eye, EyeSlash, File, FileArrowDown, FileText, FloppyDisk, FolderOpen, Gear, GithubLogo, Globe, GlobeSimple, GridFour, HardDrive, Hash, Image as Image$1, ImageSquare, Info, Key, Link as Link$2, LinkBreak, LinkSimple, List, ListBullets, ListChecks, ListNumbers, MagnifyingGlass, Minus, Monitor, Moon, Palette, PaperPlaneTilt, Paperclip, Paragraph, Pencil, PencilSimple, PlugsConnected, Plus, Prohibit, PuzzlePiece, Quotes, Rows, Ruler, ShareNetwork, Shield, ShieldCheck, ShieldWarning, SignOut, SlidersHorizontal, Sparkle, SquaresFour, Stack, Storefront, Sun, TextAlignCenter, TextAlignLeft, TextAlignRight, TextB, TextHOne, TextHThree, TextHTwo, TextItalic, TextStrikethrough, TextT, TextUnderline, ToggleLeft, Trash, Upload, User, UserCircle, UserPlus, Users, Warning, WarningCircle, WebhooksLogo, X, YoutubeLogo } from "@phosphor-icons/react";
14
14
  import { clsx } from "clsx";
15
15
  import { twMerge } from "tailwind-merge";
16
16
  import { DndContext, DragOverlay, KeyboardSensor, PointerSensor, closestCenter, rectIntersection, useDraggable, useDroppable, useSensor, useSensors } from "@dnd-kit/core";
@@ -37,7 +37,7 @@ import { Marked, Renderer } from "marked";
37
37
  import { useHotkeys } from "react-hotkeys-hook";
38
38
 
39
39
  //#region src/components/ThemeProvider.tsx
40
- const ThemeContext = React.createContext(void 0);
40
+ const ThemeContext = React$1.createContext(void 0);
41
41
  const STORAGE_KEY = "emdash-theme";
42
42
  function getSystemTheme() {
43
43
  if (typeof window === "undefined") return "light";
@@ -50,19 +50,19 @@ function getStoredTheme() {
50
50
  return "system";
51
51
  }
52
52
  function ThemeProvider({ children, defaultTheme = "system" }) {
53
- const [theme, setThemeState] = React.useState(() => {
53
+ const [theme, setThemeState] = React$1.useState(() => {
54
54
  const stored = getStoredTheme();
55
55
  return stored === "system" ? defaultTheme : stored;
56
56
  });
57
- const [resolvedTheme, setResolvedTheme] = React.useState(() => {
57
+ const [resolvedTheme, setResolvedTheme] = React$1.useState(() => {
58
58
  if (theme === "system") return getSystemTheme();
59
59
  return theme;
60
60
  });
61
- React.useEffect(() => {
61
+ React$1.useEffect(() => {
62
62
  if (theme === "system") setResolvedTheme(getSystemTheme());
63
63
  else setResolvedTheme(theme);
64
64
  }, [theme]);
65
- React.useEffect(() => {
65
+ React$1.useEffect(() => {
66
66
  if (theme !== "system") return;
67
67
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
68
68
  const handler = (e) => {
@@ -71,16 +71,16 @@ function ThemeProvider({ children, defaultTheme = "system" }) {
71
71
  mediaQuery.addEventListener("change", handler);
72
72
  return () => mediaQuery.removeEventListener("change", handler);
73
73
  }, [theme]);
74
- React.useEffect(() => {
74
+ React$1.useEffect(() => {
75
75
  const root = document.documentElement;
76
76
  root.setAttribute("data-theme", "classic");
77
77
  root.setAttribute("data-mode", resolvedTheme);
78
78
  }, [resolvedTheme]);
79
- const setTheme = React.useCallback((newTheme) => {
79
+ const setTheme = React$1.useCallback((newTheme) => {
80
80
  setThemeState(newTheme);
81
81
  localStorage.setItem(STORAGE_KEY, newTheme);
82
82
  }, []);
83
- const value = React.useMemo(() => ({
83
+ const value = React$1.useMemo(() => ({
84
84
  theme,
85
85
  setTheme,
86
86
  resolvedTheme
@@ -95,7 +95,7 @@ function ThemeProvider({ children, defaultTheme = "system" }) {
95
95
  });
96
96
  }
97
97
  function useTheme() {
98
- const context = React.useContext(ThemeContext);
98
+ const context = React$1.useContext(ThemeContext);
99
99
  if (context === void 0) throw new Error("useTheme must be used within a ThemeProvider");
100
100
  return context;
101
101
  }
@@ -334,7 +334,7 @@ function ConfirmDialog({ open, onClose, title, description, confirmLabel, pendin
334
334
  */
335
335
  function CommentDetail({ comment, onClose, onStatusChange, onDelete, isAdmin, isStatusPending }) {
336
336
  const { _: _t } = useLingui();
337
- React.useEffect(() => {
337
+ React$1.useEffect(() => {
338
338
  const handler = (e) => {
339
339
  if (e.key === "Escape" && !e.defaultPrevented) {
340
340
  e.preventDefault();
@@ -601,11 +601,11 @@ function CommentStatusBadge({ status }) {
601
601
  const PAGE_SIZE$1 = 20;
602
602
  function CommentInbox({ comments, counts, isLoading, nextCursor, collections, activeStatus, onStatusChange, collectionFilter, onCollectionFilterChange, searchQuery, onSearchChange, onCommentStatusChange, onCommentDelete, onBulkAction, onLoadMore, isAdmin, isStatusPending, deleteError, onDeleteErrorReset }) {
603
603
  const { _: _t } = useLingui();
604
- const [selected, setSelected] = React.useState(/* @__PURE__ */ new Set());
605
- const [detailComment, setDetailComment] = React.useState(null);
606
- const [deleteId, setDeleteId] = React.useState(null);
607
- const [page, setPage] = React.useState(0);
608
- React.useEffect(() => {
604
+ const [selected, setSelected] = React$1.useState(/* @__PURE__ */ new Set());
605
+ const [detailComment, setDetailComment] = React$1.useState(null);
606
+ const [deleteId, setDeleteId] = React$1.useState(null);
607
+ const [page, setPage] = React$1.useState(0);
608
+ React$1.useEffect(() => {
609
609
  setSelected(/* @__PURE__ */ new Set());
610
610
  setPage(0);
611
611
  }, [
@@ -613,7 +613,7 @@ function CommentInbox({ comments, counts, isLoading, nextCursor, collections, ac
613
613
  collectionFilter,
614
614
  searchQuery
615
615
  ]);
616
- const clearSelection = React.useCallback(() => setSelected(/* @__PURE__ */ new Set()), []);
616
+ const clearSelection = React$1.useCallback(() => setSelected(/* @__PURE__ */ new Set()), []);
617
617
  const totalPages = Math.max(1, Math.ceil(comments.length / PAGE_SIZE$1));
618
618
  const paginatedComments = comments.slice(page * PAGE_SIZE$1, (page + 1) * PAGE_SIZE$1);
619
619
  const allOnPageSelected = paginatedComments.length > 0 && paginatedComments.every((c) => selected.has(c.id));
@@ -1901,19 +1901,26 @@ async function deleteByline(id) {
1901
1901
  //#endregion
1902
1902
  //#region src/lib/api/menus.ts
1903
1903
  /**
1904
- * Menu management APIs
1904
+ * Menu management APIs.
1905
+ *
1906
+ * i18n: all endpoints accept an optional `locale`. When omitted, the server
1907
+ * returns or acts on all locales (legacy behaviour for clients that haven't
1908
+ * been updated yet).
1905
1909
  */
1910
+ function withLocale$1(path, locale) {
1911
+ return locale ? `${path}${path.includes("?") ? "&" : "?"}locale=${encodeURIComponent(locale)}` : path;
1912
+ }
1906
1913
  /**
1907
1914
  * Fetch all menus
1908
1915
  */
1909
- async function fetchMenus() {
1910
- return parseApiResponse(await apiFetch(`${API_BASE}/menus`), "Failed to fetch menus");
1916
+ async function fetchMenus(options = {}) {
1917
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus`, options.locale)), "Failed to fetch menus");
1911
1918
  }
1912
1919
  /**
1913
1920
  * Fetch a single menu with items
1914
1921
  */
1915
- async function fetchMenu(name) {
1916
- return parseApiResponse(await apiFetch(`${API_BASE}/menus/${name}`), "Failed to fetch menu");
1922
+ async function fetchMenu(name, options = {}) {
1923
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${name}`, options.locale)), "Failed to fetch menu");
1917
1924
  }
1918
1925
  /**
1919
1926
  * Create a menu
@@ -1928,8 +1935,8 @@ async function createMenu(input) {
1928
1935
  /**
1929
1936
  * Update a menu
1930
1937
  */
1931
- async function updateMenu(name, input) {
1932
- return parseApiResponse(await apiFetch(`${API_BASE}/menus/${name}`, {
1938
+ async function updateMenu(name, input, options = {}) {
1939
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${name}`, options.locale), {
1933
1940
  method: "PUT",
1934
1941
  headers: { "Content-Type": "application/json" },
1935
1942
  body: JSON.stringify(input)
@@ -1938,15 +1945,15 @@ async function updateMenu(name, input) {
1938
1945
  /**
1939
1946
  * Delete a menu
1940
1947
  */
1941
- async function deleteMenu(name) {
1942
- const response = await apiFetch(`${API_BASE}/menus/${name}`, { method: "DELETE" });
1948
+ async function deleteMenu(name, options = {}) {
1949
+ const response = await apiFetch(withLocale$1(`${API_BASE}/menus/${name}`, options.locale), { method: "DELETE" });
1943
1950
  if (!response.ok) await throwResponseError(response, "Failed to delete menu");
1944
1951
  }
1945
1952
  /**
1946
1953
  * Create a menu item
1947
1954
  */
1948
- async function createMenuItem(menuName, input) {
1949
- return parseApiResponse(await apiFetch(`${API_BASE}/menus/${menuName}/items`, {
1955
+ async function createMenuItem(menuName, input, options = {}) {
1956
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${menuName}/items`, options.locale), {
1950
1957
  method: "POST",
1951
1958
  headers: { "Content-Type": "application/json" },
1952
1959
  body: JSON.stringify(input)
@@ -1955,8 +1962,8 @@ async function createMenuItem(menuName, input) {
1955
1962
  /**
1956
1963
  * Update a menu item
1957
1964
  */
1958
- async function updateMenuItem(menuName, itemId, input) {
1959
- return parseApiResponse(await apiFetch(`${API_BASE}/menus/${menuName}/items?id=${itemId}`, {
1965
+ async function updateMenuItem(menuName, itemId, input, options = {}) {
1966
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${menuName}/items?id=${itemId}`, options.locale), {
1960
1967
  method: "PUT",
1961
1968
  headers: { "Content-Type": "application/json" },
1962
1969
  body: JSON.stringify(input)
@@ -1965,20 +1972,35 @@ async function updateMenuItem(menuName, itemId, input) {
1965
1972
  /**
1966
1973
  * Delete a menu item
1967
1974
  */
1968
- async function deleteMenuItem(menuName, itemId) {
1969
- const response = await apiFetch(`${API_BASE}/menus/${menuName}/items?id=${itemId}`, { method: "DELETE" });
1975
+ async function deleteMenuItem(menuName, itemId, options = {}) {
1976
+ const response = await apiFetch(withLocale$1(`${API_BASE}/menus/${menuName}/items?id=${itemId}`, options.locale), { method: "DELETE" });
1970
1977
  if (!response.ok) await throwResponseError(response, "Failed to delete menu item");
1971
1978
  }
1972
1979
  /**
1973
1980
  * Reorder menu items
1974
1981
  */
1975
- async function reorderMenuItems(menuName, input) {
1976
- return parseApiResponse(await apiFetch(`${API_BASE}/menus/${menuName}/reorder`, {
1982
+ async function reorderMenuItems(menuName, input, options = {}) {
1983
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${menuName}/reorder`, options.locale), {
1977
1984
  method: "POST",
1978
1985
  headers: { "Content-Type": "application/json" },
1979
1986
  body: JSON.stringify(input)
1980
1987
  }), "Failed to reorder menu items");
1981
1988
  }
1989
+ /** List every translation (locale variant) of a menu. */
1990
+ async function fetchMenuTranslations(name, options = {}) {
1991
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${name}/translations`, options.locale)), "Failed to fetch menu translations");
1992
+ }
1993
+ /**
1994
+ * Create a new locale translation of a menu. The new menu inherits the
1995
+ * source's items and label unless overridden.
1996
+ */
1997
+ async function createMenuTranslation(name, input, options = {}) {
1998
+ return parseApiResponse(await apiFetch(withLocale$1(`${API_BASE}/menus/${name}/translations`, options.locale), {
1999
+ method: "POST",
2000
+ headers: { "Content-Type": "application/json" },
2001
+ body: JSON.stringify(input)
2002
+ }), "Failed to create menu translation");
2003
+ }
1982
2004
 
1983
2005
  //#endregion
1984
2006
  //#region src/lib/api/widgets.ts
@@ -2112,19 +2134,27 @@ async function deleteSection(slug) {
2112
2134
  //#endregion
2113
2135
  //#region src/lib/api/taxonomies.ts
2114
2136
  /**
2115
- * Taxonomies API (categories, tags, custom taxonomies)
2137
+ * Taxonomies API (categories, tags, custom taxonomies).
2138
+ *
2139
+ * All endpoints are locale-aware. When no `locale` option is passed we omit
2140
+ * the query param and the server falls back to its usual resolution (no
2141
+ * filter, returning every locale — same as pre-i18n behaviour for clients
2142
+ * that haven't yet been updated).
2116
2143
  */
2144
+ function withLocale(path, locale) {
2145
+ return locale ? `${path}${path.includes("?") ? "&" : "?"}locale=${encodeURIComponent(locale)}` : path;
2146
+ }
2117
2147
  /**
2118
2148
  * Fetch all taxonomy definitions
2119
2149
  */
2120
- async function fetchTaxonomyDefs() {
2121
- return (await parseApiResponse(await apiFetch(`${API_BASE}/taxonomies`), "Failed to fetch taxonomies")).taxonomies;
2150
+ async function fetchTaxonomyDefs(options = {}) {
2151
+ return (await parseApiResponse(await apiFetch(withLocale(`${API_BASE}/taxonomies`, options.locale)), "Failed to fetch taxonomies")).taxonomies;
2122
2152
  }
2123
2153
  /**
2124
2154
  * Fetch taxonomy definition by name
2125
2155
  */
2126
- async function fetchTaxonomyDef(name) {
2127
- return (await fetchTaxonomyDefs()).find((t) => t.name === name) || null;
2156
+ async function fetchTaxonomyDef(name, options = {}) {
2157
+ return (await fetchTaxonomyDefs(options)).find((t) => t.name === name) || null;
2128
2158
  }
2129
2159
  /**
2130
2160
  * Create a custom taxonomy definition
@@ -2139,8 +2169,8 @@ async function createTaxonomy(input) {
2139
2169
  /**
2140
2170
  * Fetch terms for a taxonomy
2141
2171
  */
2142
- async function fetchTerms(taxonomyName) {
2143
- return (await parseApiResponse(await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms`), "Failed to fetch terms")).terms;
2172
+ async function fetchTerms(taxonomyName, options = {}) {
2173
+ return (await parseApiResponse(await apiFetch(withLocale(`${API_BASE}/taxonomies/${taxonomyName}/terms`, options.locale)), "Failed to fetch terms")).terms;
2144
2174
  }
2145
2175
  /**
2146
2176
  * Create a term
@@ -2155,8 +2185,8 @@ async function createTerm(taxonomyName, input) {
2155
2185
  /**
2156
2186
  * Update a term
2157
2187
  */
2158
- async function updateTerm(taxonomyName, slug, input) {
2159
- return (await parseApiResponse(await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}`, {
2188
+ async function updateTerm(taxonomyName, slug, input, options = {}) {
2189
+ return (await parseApiResponse(await apiFetch(withLocale(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}`, options.locale), {
2160
2190
  method: "PUT",
2161
2191
  headers: { "Content-Type": "application/json" },
2162
2192
  body: JSON.stringify(input)
@@ -2165,10 +2195,25 @@ async function updateTerm(taxonomyName, slug, input) {
2165
2195
  /**
2166
2196
  * Delete a term
2167
2197
  */
2168
- async function deleteTerm(taxonomyName, slug) {
2169
- const response = await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}`, { method: "DELETE" });
2198
+ async function deleteTerm(taxonomyName, slug, options = {}) {
2199
+ const response = await apiFetch(withLocale(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}`, options.locale), { method: "DELETE" });
2170
2200
  if (!response.ok) await throwResponseError(response, "Failed to delete term");
2171
2201
  }
2202
+ /** List every translation (locale variant) of a term. */
2203
+ async function fetchTermTranslations(taxonomyName, slug, options = {}) {
2204
+ return parseApiResponse(await apiFetch(withLocale(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}/translations`, options.locale)), "Failed to fetch term translations");
2205
+ }
2206
+ /**
2207
+ * Create a new locale translation of a term. The new term inherits slug,
2208
+ * label, parent, and description from the source unless overridden in `input`.
2209
+ */
2210
+ async function createTermTranslation(taxonomyName, slug, input, options = {}) {
2211
+ return (await parseApiResponse(await apiFetch(withLocale(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}/translations`, options.locale), {
2212
+ method: "POST",
2213
+ headers: { "Content-Type": "application/json" },
2214
+ body: JSON.stringify(input)
2215
+ }), "Failed to create term translation")).term;
2216
+ }
2172
2217
 
2173
2218
  //#endregion
2174
2219
  //#region src/lib/api/import.ts
@@ -2715,6 +2760,44 @@ function fromDatetimeLocalInputValue(value) {
2715
2760
  return value === "" ? "" : `${value}:00.000Z`;
2716
2761
  }
2717
2762
 
2763
+ //#endregion
2764
+ //#region src/lib/media-utils.ts
2765
+ function providerItemToMediaItem(providerId, item) {
2766
+ return {
2767
+ id: item.id,
2768
+ filename: item.filename,
2769
+ mimeType: item.mimeType,
2770
+ url: item.previewUrl || "",
2771
+ size: item.size || 0,
2772
+ width: item.width,
2773
+ height: item.height,
2774
+ alt: item.alt,
2775
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2776
+ provider: providerId,
2777
+ meta: item.meta
2778
+ };
2779
+ }
2780
+ function getFileIcon(mimeType) {
2781
+ if (mimeType.startsWith("video/")) return "🎬";
2782
+ if (mimeType.startsWith("audio/")) return "🎵";
2783
+ if (mimeType.includes("pdf")) return "📄";
2784
+ if (mimeType.includes("document") || mimeType.includes("word")) return "📝";
2785
+ if (mimeType.includes("spreadsheet") || mimeType.includes("excel")) return "📊";
2786
+ return "📁";
2787
+ }
2788
+ function formatFileSize(bytes) {
2789
+ if (bytes === 0) return "0 B";
2790
+ const k = 1024;
2791
+ const sizes = [
2792
+ "B",
2793
+ "KB",
2794
+ "MB",
2795
+ "GB"
2796
+ ];
2797
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
2798
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
2799
+ }
2800
+
2718
2801
  //#endregion
2719
2802
  //#region src/lib/url.ts
2720
2803
  /**
@@ -2767,44 +2850,6 @@ function safeIconUrl(url, width) {
2767
2850
  }
2768
2851
  }
2769
2852
 
2770
- //#endregion
2771
- //#region src/lib/media-utils.ts
2772
- function providerItemToMediaItem(providerId, item) {
2773
- return {
2774
- id: item.id,
2775
- filename: item.filename,
2776
- mimeType: item.mimeType,
2777
- url: item.previewUrl || "",
2778
- size: item.size || 0,
2779
- width: item.width,
2780
- height: item.height,
2781
- alt: item.alt,
2782
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2783
- provider: providerId,
2784
- meta: item.meta
2785
- };
2786
- }
2787
- function getFileIcon(mimeType) {
2788
- if (mimeType.startsWith("video/")) return "🎬";
2789
- if (mimeType.startsWith("audio/")) return "🎵";
2790
- if (mimeType.includes("pdf")) return "📄";
2791
- if (mimeType.includes("document") || mimeType.includes("word")) return "📝";
2792
- if (mimeType.includes("spreadsheet") || mimeType.includes("excel")) return "📊";
2793
- return "📁";
2794
- }
2795
- function formatFileSize(bytes) {
2796
- if (bytes === 0) return "0 B";
2797
- const k = 1024;
2798
- const sizes = [
2799
- "B",
2800
- "KB",
2801
- "MB",
2802
- "GB"
2803
- ];
2804
- const i = Math.floor(Math.log(bytes) / Math.log(k));
2805
- return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i];
2806
- }
2807
-
2808
2853
  //#endregion
2809
2854
  //#region src/components/MediaPickerModal.tsx
2810
2855
  /**
@@ -2832,22 +2877,41 @@ function probeImageDimensions(url) {
2832
2877
  img.src = url;
2833
2878
  });
2834
2879
  }
2835
- function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "image/", title: providedTitle }) {
2880
+ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "image/", title: providedTitle, hideUrlInput = false, mediaKind = "image" }) {
2836
2881
  const { _: _t } = useLingui();
2837
- const title = providedTitle ?? _t({
2882
+ const isFileKind = mediaKind === "file";
2883
+ const title = providedTitle ?? (isFileKind ? _t({
2884
+ id: "+Xjyeo",
2885
+ message: "Select File"
2886
+ }) : _t({
2838
2887
  id: "bAiEJP",
2839
2888
  message: "Select Image"
2889
+ }));
2890
+ const emptyStateUploadHint = isFileKind ? _t({
2891
+ id: "R5f0DH",
2892
+ message: "Upload a file to get started"
2893
+ }) : _t({
2894
+ id: "pzjc9K",
2895
+ message: "Upload an image to get started"
2896
+ });
2897
+ const emptyStateUploadCta = isFileKind ? _t({
2898
+ id: "IQ3gAw",
2899
+ message: "Upload File"
2900
+ }) : _t({
2901
+ id: "lnCMdg",
2902
+ message: "Upload Image"
2840
2903
  });
2904
+ const EmptyStateIcon = isFileKind ? Paperclip : Image$1;
2841
2905
  const queryClient = useQueryClient();
2842
- const [selectedItem, setSelectedItem] = React.useState(null);
2843
- const [activeProvider, setActiveProvider] = React.useState("local");
2844
- const [searchQuery, setSearchQuery] = React.useState("");
2845
- const fileInputRef = React.useRef(null);
2846
- const [imageUrl, setImageUrl] = React.useState("");
2847
- const [isProbing, setIsProbing] = React.useState(false);
2848
- const [urlError, setUrlError] = React.useState(null);
2849
- const [providerDimensions, setProviderDimensions] = React.useState({});
2850
- React.useEffect(() => {
2906
+ const [selectedItem, setSelectedItem] = React$1.useState(null);
2907
+ const [activeProvider, setActiveProvider] = React$1.useState("local");
2908
+ const [searchQuery, setSearchQuery] = React$1.useState("");
2909
+ const fileInputRef = React$1.useRef(null);
2910
+ const [imageUrl, setImageUrl] = React$1.useState("");
2911
+ const [isProbing, setIsProbing] = React$1.useState(false);
2912
+ const [urlError, setUrlError] = React$1.useState(null);
2913
+ const [providerDimensions, setProviderDimensions] = React$1.useState({});
2914
+ React$1.useEffect(() => {
2851
2915
  if (open) {
2852
2916
  setSelectedItem(null);
2853
2917
  setActiveProvider("local");
@@ -2864,7 +2928,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
2864
2928
  enabled: open,
2865
2929
  placeholderData: []
2866
2930
  });
2867
- const activeProviderInfo = React.useMemo(() => {
2931
+ const activeProviderInfo = React$1.useMemo(() => {
2868
2932
  if (activeProvider === "local") return {
2869
2933
  id: "local",
2870
2934
  name: "Library",
@@ -2901,7 +2965,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
2901
2965
  enabled: open && activeProvider !== "local"
2902
2966
  });
2903
2967
  const isLoading = activeProvider === "local" ? localLoading : providerLoading;
2904
- const [uploadError, setUploadError] = React.useState(null);
2968
+ const [uploadError, setUploadError] = React$1.useState(null);
2905
2969
  const uploadLocalMutation = useMutation({
2906
2970
  mutationFn: (file) => uploadMedia(file),
2907
2971
  onSuccess: (item) => {
@@ -2931,7 +2995,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
2931
2995
  }
2932
2996
  });
2933
2997
  const isUploading = uploadLocalMutation.isPending || uploadProviderMutation.isPending;
2934
- const updatedDimensionsRef = React.useRef(/* @__PURE__ */ new Set());
2998
+ const updatedDimensionsRef = React$1.useRef(/* @__PURE__ */ new Set());
2935
2999
  const dimensionsMutation = useMutation({
2936
3000
  mutationFn: ({ id, width, height }) => updateMedia(id, {
2937
3001
  width,
@@ -2962,7 +3026,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
2962
3026
  console.warn("Failed to update media dimensions:", error);
2963
3027
  }
2964
3028
  });
2965
- const handleDimensionsDetected = React.useCallback((id, width, height) => {
3029
+ const handleDimensionsDetected = React$1.useCallback((id, width, height) => {
2966
3030
  if (updatedDimensionsRef.current.has(id)) return;
2967
3031
  updatedDimensionsRef.current.add(id);
2968
3032
  dimensionsMutation.mutate({
@@ -2971,7 +3035,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
2971
3035
  height
2972
3036
  });
2973
3037
  }, [dimensionsMutation]);
2974
- const items = React.useMemo(() => {
3038
+ const items = React$1.useMemo(() => {
2975
3039
  if (activeProvider === "local") {
2976
3040
  const localItems = localData?.items || [];
2977
3041
  if (!mimeTypeFilter) return localItems;
@@ -3064,7 +3128,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3064
3128
  };
3065
3129
  const canUpload = activeProvider === "local" || (activeProviderInfo?.capabilities.upload ?? false);
3066
3130
  const canSearch = activeProviderInfo?.capabilities.search ?? false;
3067
- const providerTabs = React.useMemo(() => {
3131
+ const providerTabs = React$1.useMemo(() => {
3068
3132
  const tabs = [{
3069
3133
  id: "local",
3070
3134
  name: "Library",
@@ -3115,7 +3179,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3115
3179
  })
3116
3180
  })]
3117
3181
  }),
3118
- /* @__PURE__ */ jsxs("div", {
3182
+ !hideUrlInput && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
3119
3183
  className: "border-b pb-4",
3120
3184
  children: [
3121
3185
  /* @__PURE__ */ jsx(Label, { children: _t({
@@ -3155,8 +3219,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3155
3219
  children: urlError
3156
3220
  })
3157
3221
  ]
3158
- }),
3159
- /* @__PURE__ */ jsxs("div", {
3222
+ }), /* @__PURE__ */ jsxs("div", {
3160
3223
  className: "relative py-2",
3161
3224
  children: [/* @__PURE__ */ jsx("div", {
3162
3225
  className: "absolute inset-0 flex items-center",
@@ -3171,7 +3234,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3171
3234
  })
3172
3235
  })
3173
3236
  })]
3174
- }),
3237
+ })] }),
3175
3238
  providerTabs.length > 1 && /* @__PURE__ */ jsx("div", {
3176
3239
  className: "flex gap-2 border-b pb-3 flex-wrap",
3177
3240
  children: providerTabs.map((tab) => /* @__PURE__ */ jsxs("button", {
@@ -3255,7 +3318,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3255
3318
  }) : items.length === 0 ? /* @__PURE__ */ jsxs("div", {
3256
3319
  className: "flex flex-col items-center justify-center h-full text-center p-8",
3257
3320
  children: [
3258
- /* @__PURE__ */ jsx(Image$1, {
3321
+ /* @__PURE__ */ jsx(EmptyStateIcon, {
3259
3322
  className: "h-12 w-12 text-kumo-subtle mb-4",
3260
3323
  "aria-hidden": "true"
3261
3324
  }),
@@ -3271,10 +3334,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3271
3334
  children: canSearch && searchQuery ? _t({
3272
3335
  id: "jKoZME",
3273
3336
  message: "Try a different search term"
3274
- }) : canUpload ? _t({
3275
- id: "pzjc9K",
3276
- message: "Upload an image to get started"
3277
- }) : _t({
3337
+ }) : canUpload ? emptyStateUploadHint : _t({
3278
3338
  id: "IbZjS1",
3279
3339
  message: "No media available from this provider"
3280
3340
  })
@@ -3283,10 +3343,7 @@ function MediaPickerModal({ open, onOpenChange, onSelect, mimeTypeFilter = "imag
3283
3343
  className: "mt-4",
3284
3344
  icon: /* @__PURE__ */ jsx(Upload, {}),
3285
3345
  onClick: () => fileInputRef.current?.click(),
3286
- children: _t({
3287
- id: "lnCMdg",
3288
- message: "Upload Image"
3289
- })
3346
+ children: emptyStateUploadCta
3290
3347
  })
3291
3348
  ]
3292
3349
  }) : /* @__PURE__ */ jsx("ul", {
@@ -3384,7 +3441,7 @@ function MediaPickerItem({ item, selected, onClick, onDoubleClick, onDimensionsD
3384
3441
  const { _: _t2 } = useLingui();
3385
3442
  const isImage = item.mimeType.startsWith("image/");
3386
3443
  const needsDimensions = isImage && (!item.width || !item.height);
3387
- const handleImageLoad = React.useCallback((e) => {
3444
+ const handleImageLoad = React$1.useCallback((e) => {
3388
3445
  if (needsDimensions && onDimensionsDetected) {
3389
3446
  const img = e.currentTarget;
3390
3447
  if (img.naturalWidth && img.naturalHeight) onDimensionsDetected(item.id, img.naturalWidth, img.naturalHeight);
@@ -3451,7 +3508,7 @@ function ProviderMediaItem({ item, selected, onClick, onDoubleClick, onDimension
3451
3508
  const { _: _t3 } = useLingui();
3452
3509
  const isImage = item.mimeType.startsWith("image/");
3453
3510
  const needsDimensions = isImage && (!item.width || !item.height);
3454
- const handleImageLoad = React.useCallback((e) => {
3511
+ const handleImageLoad = React$1.useCallback((e) => {
3455
3512
  if (needsDimensions && onDimensionsLoaded) {
3456
3513
  const img = e.currentTarget;
3457
3514
  if (img.naturalWidth && img.naturalHeight) onDimensionsLoaded(img.naturalWidth, img.naturalHeight);
@@ -3524,7 +3581,7 @@ function ProviderMediaItem({ item, selected, onClick, onDoubleClick, onDimension
3524
3581
  */
3525
3582
  function BlockKitMediaPickerField({ actionId, label, placeholder, mimeTypeFilter, value, onChange }) {
3526
3583
  const { _: _t } = useLingui();
3527
- const [pickerOpen, setPickerOpen] = React.useState(false);
3584
+ const [pickerOpen, setPickerOpen] = React$1.useState(false);
3528
3585
  const url = typeof value === "string" && value.length > 0 ? value : "";
3529
3586
  const filter = mimeTypeFilter ?? "image/";
3530
3587
  const canPreview = isSafePreviewUrl(url);
@@ -3625,9 +3682,9 @@ function isSafePreviewUrl(url) {
3625
3682
  */
3626
3683
  function BlockKitFieldWidget({ label, elements, value, onChange }) {
3627
3684
  const obj = value && typeof value === "object" ? value : {};
3628
- const objRef = React.useRef(obj);
3685
+ const objRef = React$1.useRef(obj);
3629
3686
  objRef.current = obj;
3630
- const handleElementChange = React.useCallback((actionId, elementValue) => {
3687
+ const handleElementChange = React$1.useCallback((actionId, elementValue) => {
3631
3688
  onChange({
3632
3689
  ...objRef.current,
3633
3690
  [actionId]: elementValue
@@ -3765,10 +3822,10 @@ function findCurrentHeading(headings, cursorPos) {
3765
3822
  */
3766
3823
  function DocumentOutline({ editor, className }) {
3767
3824
  const { _: _t } = useLingui();
3768
- const [isExpanded, setIsExpanded] = React.useState(true);
3769
- const [headings, setHeadings] = React.useState([]);
3770
- const [currentPos, setCurrentPos] = React.useState(0);
3771
- React.useEffect(() => {
3825
+ const [isExpanded, setIsExpanded] = React$1.useState(true);
3826
+ const [headings, setHeadings] = React$1.useState([]);
3827
+ const [currentPos, setCurrentPos] = React$1.useState(0);
3828
+ React$1.useEffect(() => {
3772
3829
  if (!editor) return;
3773
3830
  const updateHeadings = () => {
3774
3831
  setHeadings(extractHeadings(editor));
@@ -3779,7 +3836,7 @@ function DocumentOutline({ editor, className }) {
3779
3836
  editor.off("update", updateHeadings);
3780
3837
  };
3781
3838
  }, [editor]);
3782
- React.useEffect(() => {
3839
+ React$1.useEffect(() => {
3783
3840
  if (!editor) return;
3784
3841
  const updatePosition = () => {
3785
3842
  const { from } = editor.state.selection;
@@ -3841,7 +3898,7 @@ function DocumentOutline({ editor, className }) {
3841
3898
  * Error boundary that wraps trusted plugin field widgets.
3842
3899
  * On render error, shows a warning with a retry button instead of crashing the editor.
3843
3900
  */
3844
- var PluginFieldErrorBoundary = class extends React.Component {
3901
+ var PluginFieldErrorBoundary = class extends React$1.Component {
3845
3902
  constructor(props) {
3846
3903
  super(props);
3847
3904
  this.state = { hasError: false };
@@ -3902,9 +3959,9 @@ function stripKeys$1(items) {
3902
3959
  function RepeaterField({ label, id, value, onChange, subFields, minItems = 0, maxItems }) {
3903
3960
  const { _: _t } = useLingui();
3904
3961
  const rawItems = Array.isArray(value) ? value : [];
3905
- const [items, setItems] = React.useState(() => ensureKeys$1(rawItems));
3906
- const [collapsedItems, setCollapsedItems] = React.useState(/* @__PURE__ */ new Set());
3907
- React.useEffect(() => {
3962
+ const [items, setItems] = React$1.useState(() => ensureKeys$1(rawItems));
3963
+ const [collapsedItems, setCollapsedItems] = React$1.useState(/* @__PURE__ */ new Set());
3964
+ React$1.useEffect(() => {
3908
3965
  const incoming = Array.isArray(value) ? value : [];
3909
3966
  setItems((prev) => incoming.map((item, i) => {
3910
3967
  const obj = typeof item === "object" && item !== null ? item : {};
@@ -4157,11 +4214,11 @@ function SubFieldInput({ subField, value, onChange }) {
4157
4214
  * callback identity changes.
4158
4215
  */
4159
4216
  function useStableCallback(callback) {
4160
- const ref = React.useRef(callback);
4161
- React.useLayoutEffect(() => {
4217
+ const ref = React$1.useRef(callback);
4218
+ React$1.useLayoutEffect(() => {
4162
4219
  ref.current = callback;
4163
4220
  });
4164
- return React.useCallback((...args) => ref.current(...args), []);
4221
+ return React$1.useCallback((...args) => ref.current(...args), []);
4165
4222
  }
4166
4223
  /**
4167
4224
  * Returns a debounced version of a value that only updates after the
@@ -4169,8 +4226,8 @@ function useStableCallback(callback) {
4169
4226
  * inputs that trigger API calls.
4170
4227
  */
4171
4228
  function useDebouncedValue$1(value, delay) {
4172
- const [debouncedValue, setDebouncedValue] = React.useState(value);
4173
- React.useEffect(() => {
4229
+ const [debouncedValue, setDebouncedValue] = React$1.useState(value);
4230
+ React$1.useEffect(() => {
4174
4231
  const timer = setTimeout(setDebouncedValue, delay, value);
4175
4232
  return () => clearTimeout(timer);
4176
4233
  }, [value, delay]);
@@ -4192,13 +4249,13 @@ function useDebouncedValue$1(value, delay) {
4192
4249
  */
4193
4250
  function ImageDetailPanel({ attributes, onUpdate, onReplace, onDelete, onClose, inline = false }) {
4194
4251
  const { _: _t } = useLingui();
4195
- const [alt, setAlt] = React.useState(attributes.alt ?? "");
4196
- const [caption, setCaption] = React.useState(attributes.caption ?? "");
4197
- const [title, setTitle] = React.useState(attributes.title ?? "");
4198
- const [showMediaPicker, setShowMediaPicker] = React.useState(false);
4199
- const [displayWidth, setDisplayWidth] = React.useState(attributes.displayWidth ?? attributes.width);
4200
- const [displayHeight, setDisplayHeight] = React.useState(attributes.displayHeight ?? attributes.height);
4201
- const [lockAspectRatio, setLockAspectRatio] = React.useState(true);
4252
+ const [alt, setAlt] = React$1.useState(attributes.alt ?? "");
4253
+ const [caption, setCaption] = React$1.useState(attributes.caption ?? "");
4254
+ const [title, setTitle] = React$1.useState(attributes.title ?? "");
4255
+ const [showMediaPicker, setShowMediaPicker] = React$1.useState(false);
4256
+ const [displayWidth, setDisplayWidth] = React$1.useState(attributes.displayWidth ?? attributes.width);
4257
+ const [displayHeight, setDisplayHeight] = React$1.useState(attributes.displayHeight ?? attributes.height);
4258
+ const [lockAspectRatio, setLockAspectRatio] = React$1.useState(true);
4202
4259
  const aspectRatio = attributes.width && attributes.height ? attributes.width / attributes.height : void 0;
4203
4260
  const handleWidthChange = (value) => {
4204
4261
  const newWidth = value ? parseInt(value, 10) : void 0;
@@ -4227,7 +4284,7 @@ function ImageDetailPanel({ attributes, onUpdate, onReplace, onDelete, onClose,
4227
4284
  setShowMediaPicker(false);
4228
4285
  onClose();
4229
4286
  };
4230
- const hasChanges = React.useMemo(() => {
4287
+ const hasChanges = React$1.useMemo(() => {
4231
4288
  const originalDisplayWidth = attributes.displayWidth ?? attributes.width;
4232
4289
  const originalDisplayHeight = attributes.displayHeight ?? attributes.height;
4233
4290
  return alt !== (attributes.alt ?? "") || caption !== (attributes.caption ?? "") || title !== (attributes.title ?? "") || displayWidth !== originalDisplayWidth || displayHeight !== originalDisplayHeight;
@@ -4249,13 +4306,13 @@ function ImageDetailPanel({ attributes, onUpdate, onReplace, onDelete, onClose,
4249
4306
  });
4250
4307
  onClose();
4251
4308
  };
4252
- const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
4309
+ const [showDeleteConfirm, setShowDeleteConfirm] = React$1.useState(false);
4253
4310
  const handleDelete = () => {
4254
4311
  setShowDeleteConfirm(true);
4255
4312
  };
4256
4313
  const stableOnClose = useStableCallback(onClose);
4257
4314
  const stableHandleSave = useStableCallback(handleSave);
4258
- React.useEffect(() => {
4315
+ React$1.useEffect(() => {
4259
4316
  const handleKeyDown = (e) => {
4260
4317
  if (e.key === "Escape") stableOnClose();
4261
4318
  if ((e.metaKey || e.ctrlKey) && e.key === "s") {
@@ -5197,8 +5254,8 @@ const blockTransforms = [
5197
5254
  */
5198
5255
  function BlockMenu({ editor, anchorElement, isOpen, onClose }) {
5199
5256
  const { _: _t } = useLingui();
5200
- const [showTransforms, setShowTransforms] = React.useState(false);
5201
- const menuRef = React.useRef(null);
5257
+ const [showTransforms, setShowTransforms] = React$1.useState(false);
5258
+ const menuRef = React$1.useRef(null);
5202
5259
  const stableOnClose = useStableCallback(onClose);
5203
5260
  const { refs, floatingStyles } = useFloating({
5204
5261
  open: isOpen,
@@ -5213,10 +5270,10 @@ function BlockMenu({ editor, anchorElement, isOpen, onClose }) {
5213
5270
  ],
5214
5271
  whileElementsMounted: autoUpdate
5215
5272
  });
5216
- React.useEffect(() => {
5273
+ React$1.useEffect(() => {
5217
5274
  if (anchorElement) refs.setReference(anchorElement);
5218
5275
  }, [anchorElement, refs]);
5219
- React.useEffect(() => {
5276
+ React$1.useEffect(() => {
5220
5277
  if (!isOpen) return;
5221
5278
  const handleKeyDown = (e) => {
5222
5279
  if (e.key === "Escape") {
@@ -5232,7 +5289,7 @@ function BlockMenu({ editor, anchorElement, isOpen, onClose }) {
5232
5289
  stableOnClose,
5233
5290
  showTransforms
5234
5291
  ]);
5235
- React.useEffect(() => {
5292
+ React$1.useEffect(() => {
5236
5293
  if (!isOpen) return;
5237
5294
  const handleClickOutside = (e) => {
5238
5295
  const target = e.target;
@@ -5248,7 +5305,7 @@ function BlockMenu({ editor, anchorElement, isOpen, onClose }) {
5248
5305
  document.removeEventListener("mousedown", handleClickOutside);
5249
5306
  };
5250
5307
  }, [isOpen, stableOnClose]);
5251
- React.useEffect(() => {
5308
+ React$1.useEffect(() => {
5252
5309
  if (!isOpen) setShowTransforms(false);
5253
5310
  }, [isOpen]);
5254
5311
  const handleDuplicate = () => {
@@ -5341,11 +5398,11 @@ function BlockMenu({ editor, anchorElement, isOpen, onClose }) {
5341
5398
  * DragHandleWrapper - Official TipTap drag handle with BlockMenu integration
5342
5399
  */
5343
5400
  function DragHandleWrapper({ editor }) {
5344
- const [hoveredNode, setHoveredNode] = React.useState(null);
5345
- const [menuOpen, setMenuOpen] = React.useState(false);
5346
- const [menuAnchor, setMenuAnchor] = React.useState(null);
5347
- const handleRef = React.useRef(null);
5348
- const handleClick = React.useCallback((e) => {
5401
+ const [hoveredNode, setHoveredNode] = React$1.useState(null);
5402
+ const [menuOpen, setMenuOpen] = React$1.useState(false);
5403
+ const [menuAnchor, setMenuAnchor] = React$1.useState(null);
5404
+ const handleRef = React$1.useRef(null);
5405
+ const handleClick = React$1.useCallback((e) => {
5349
5406
  e.preventDefault();
5350
5407
  e.stopPropagation();
5351
5408
  if (!hoveredNode) return;
@@ -5354,21 +5411,21 @@ function DragHandleWrapper({ editor }) {
5354
5411
  setMenuOpen(true);
5355
5412
  editor.commands.lockDragHandle();
5356
5413
  }, [editor, hoveredNode]);
5357
- const handleCloseMenu = React.useCallback(() => {
5414
+ const handleCloseMenu = React$1.useCallback(() => {
5358
5415
  setMenuOpen(false);
5359
5416
  setMenuAnchor(null);
5360
5417
  editor.commands.unlockDragHandle();
5361
5418
  }, [editor]);
5362
5419
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DragHandle, {
5363
5420
  editor,
5364
- onNodeChange: React.useCallback((data) => {
5421
+ onNodeChange: React$1.useCallback((data) => {
5365
5422
  if (data.node) setHoveredNode({
5366
5423
  node: data.node,
5367
5424
  pos: data.pos
5368
5425
  });
5369
5426
  else if (!menuOpen) setHoveredNode(null);
5370
5427
  }, [menuOpen]),
5371
- computePositionConfig: React.useMemo(() => ({
5428
+ computePositionConfig: React$1.useMemo(() => ({
5372
5429
  placement: "left-start",
5373
5430
  strategy: "absolute"
5374
5431
  }), []),
@@ -5402,10 +5459,10 @@ function DragHandleWrapper({ editor }) {
5402
5459
  * - Delete/replace options
5403
5460
  */
5404
5461
  function ImageNodeView({ node, updateAttributes, selected, deleteNode, editor }) {
5405
- const [isEditingAlt, setIsEditingAlt] = React.useState(false);
5406
- const [altText, setAltText] = React.useState(node.attrs.alt || "");
5462
+ const [isEditingAlt, setIsEditingAlt] = React$1.useState(false);
5463
+ const [altText, setAltText] = React$1.useState(node.attrs.alt || "");
5407
5464
  /** Whether this node currently has its sidebar panel open */
5408
- const sidebarOpenRef = React.useRef(false);
5465
+ const sidebarOpenRef = React$1.useRef(false);
5409
5466
  const handleSaveAlt = () => {
5410
5467
  updateAttributes({ alt: altText });
5411
5468
  setIsEditingAlt(false);
@@ -5419,7 +5476,7 @@ function ImageNodeView({ node, updateAttributes, selected, deleteNode, editor })
5419
5476
  setIsEditingAlt(false);
5420
5477
  }
5421
5478
  };
5422
- React.useEffect(() => {
5479
+ React$1.useEffect(() => {
5423
5480
  setAltText(node.attrs.alt || "");
5424
5481
  }, [node.attrs.alt]);
5425
5482
  const getImageAttrs = () => ({
@@ -5461,7 +5518,7 @@ function ImageNodeView({ node, updateAttributes, selected, deleteNode, editor })
5461
5518
  if (sidebarOpenRef.current) closeSidebar();
5462
5519
  else openSidebar();
5463
5520
  };
5464
- React.useEffect(() => {
5521
+ React$1.useEffect(() => {
5465
5522
  if (!selected) closeSidebar();
5466
5523
  }, [selected]);
5467
5524
  return /* @__PURE__ */ jsx(NodeViewWrapper, {
@@ -5782,10 +5839,10 @@ function PluginBlockNodeView({ node, updateAttributes, selected, deleteNode, edi
5782
5839
  const { Icon, label, color, placeholder } = getEmbedMeta(blockType, registry);
5783
5840
  const blockDef = registry.get(blockType);
5784
5841
  const hasFields = blockDef?.fields && blockDef.fields.length > 0;
5785
- const [isEditing, setIsEditing] = React.useState(false);
5786
- const [editValue, setEditValue] = React.useState(id || "");
5787
- const inputRef = React.useRef(null);
5788
- React.useEffect(() => {
5842
+ const [isEditing, setIsEditing] = React$1.useState(false);
5843
+ const [editValue, setEditValue] = React$1.useState(id || "");
5844
+ const inputRef = React$1.useRef(null);
5845
+ React$1.useEffect(() => {
5789
5846
  if (isEditing) {
5790
5847
  setEditValue(id || "");
5791
5848
  setTimeout(() => inputRef.current?.focus(), 0);
@@ -6006,7 +6063,7 @@ const PluginBlockExtension = Node$1.create({
6006
6063
  */
6007
6064
  function SectionPickerModal({ open, onOpenChange, onSelect }) {
6008
6065
  const { _: _t } = useLingui();
6009
- const [searchQuery, setSearchQuery] = React.useState("");
6066
+ const [searchQuery, setSearchQuery] = React$1.useState("");
6010
6067
  const debouncedSearch = useDebouncedValue$1(searchQuery, 300);
6011
6068
  const { data: sectionsData, isLoading: sectionsLoading } = useQuery({
6012
6069
  queryKey: ["sections", { search: debouncedSearch }],
@@ -6014,7 +6071,7 @@ function SectionPickerModal({ open, onOpenChange, onSelect }) {
6014
6071
  enabled: open
6015
6072
  });
6016
6073
  const sections = sectionsData?.items ?? [];
6017
- React.useEffect(() => {
6074
+ React$1.useEffect(() => {
6018
6075
  if (open) setSearchQuery("");
6019
6076
  }, [open]);
6020
6077
  const handleSelect = (section) => {
@@ -6805,7 +6862,7 @@ function createSlashCommandsExtension(options) {
6805
6862
  */
6806
6863
  function SlashCommandMenu({ state, onCommand, onClose: _onClose, setSelectedIndex }) {
6807
6864
  const { _: _t } = useLingui();
6808
- const containerRef = React.useRef(null);
6865
+ const containerRef = React$1.useRef(null);
6809
6866
  const { refs, floatingStyles } = useFloating({
6810
6867
  open: state.isOpen,
6811
6868
  placement: "bottom-start",
@@ -6816,13 +6873,13 @@ function SlashCommandMenu({ state, onCommand, onClose: _onClose, setSelectedInde
6816
6873
  ],
6817
6874
  whileElementsMounted: autoUpdate
6818
6875
  });
6819
- React.useEffect(() => {
6876
+ React$1.useEffect(() => {
6820
6877
  if (state.clientRect) {
6821
6878
  const clientRectFn = state.clientRect;
6822
6879
  refs.setReference({ getBoundingClientRect: () => clientRectFn() ?? new DOMRect() });
6823
6880
  }
6824
6881
  }, [state.clientRect, refs]);
6825
- React.useEffect(() => {
6882
+ React$1.useEffect(() => {
6826
6883
  if (!state.isOpen) return;
6827
6884
  const container = containerRef.current;
6828
6885
  if (!container) return;
@@ -6886,9 +6943,9 @@ function hasPluginBlockFormData(values) {
6886
6943
  * Otherwise falls back to a simple URL input.
6887
6944
  */
6888
6945
  function PluginBlockModal({ block, initialValues, onClose, onInsert }) {
6889
- const [formValues, setFormValues] = React.useState({});
6890
- const inputRef = React.useRef(null);
6891
- React.useEffect(() => {
6946
+ const [formValues, setFormValues] = React$1.useState({});
6947
+ const inputRef = React$1.useRef(null);
6948
+ React$1.useEffect(() => {
6892
6949
  if (block) {
6893
6950
  setFormValues(buildPluginBlockFormValues(block, initialValues));
6894
6951
  if (!block.fields || block.fields.length === 0) setTimeout(() => inputRef.current?.focus(), 0);
@@ -7074,12 +7131,12 @@ function stripKeys(items) {
7074
7131
  }
7075
7132
  function BlockKitRepeater({ field, pluginId, value, onChange }) {
7076
7133
  const { _: _t2 } = useLingui();
7077
- const rawItems = React.useMemo(() => Array.isArray(value) ? value : [], [value]);
7078
- const [items, setItems] = React.useState(() => ensureKeys(rawItems));
7079
- const [expanded, setExpanded] = React.useState(/* @__PURE__ */ new Set());
7134
+ const rawItems = React$1.useMemo(() => Array.isArray(value) ? value : [], [value]);
7135
+ const [items, setItems] = React$1.useState(() => ensureKeys(rawItems));
7136
+ const [expanded, setExpanded] = React$1.useState(/* @__PURE__ */ new Set());
7080
7137
  const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
7081
- const lastEmittedRef = React.useRef(null);
7082
- React.useEffect(() => {
7138
+ const lastEmittedRef = React$1.useRef(null);
7139
+ React$1.useEffect(() => {
7083
7140
  if (lastEmittedRef.current === rawItems) return;
7084
7141
  setItems((prev) => rawItems.map((item, i) => {
7085
7142
  const obj = typeof item === "object" && item !== null ? item : {};
@@ -7291,9 +7348,9 @@ function BlockKitRepeaterItem({ item, index, fields, pluginId, isCollapsed, onTo
7291
7348
  * When `optionsRoute` is set, fetches `{ items: [{ id, name }] }` from the plugin route.
7292
7349
  */
7293
7350
  function DynamicSelect({ field, pluginId, value, onChange }) {
7294
- const [dynamicOptions, setDynamicOptions] = React.useState(null);
7295
- const [loading, setLoading] = React.useState(false);
7296
- React.useEffect(() => {
7351
+ const [dynamicOptions, setDynamicOptions] = React$1.useState(null);
7352
+ const [loading, setLoading] = React$1.useState(false);
7353
+ React$1.useEffect(() => {
7297
7354
  if (!field.optionsRoute || !pluginId) return;
7298
7355
  const controller = new AbortController();
7299
7356
  setLoading(true);
@@ -7385,34 +7442,34 @@ function EditorFooter({ editor }) {
7385
7442
  */
7386
7443
  function PortableTextEditor({ value, onChange, placeholder = "Start writing...", className, editable = true, "aria-labelledby": ariaLabelledby, pluginBlocks = [], focusMode: controlledFocusMode, onFocusModeChange, onEditorReady, minimal = false, onBlockSidebarOpen, onBlockSidebarClose }) {
7387
7444
  const { _: _t4 } = useLingui();
7388
- const onChangeRef = React.useRef(onChange);
7389
- React.useEffect(() => {
7445
+ const onChangeRef = React$1.useRef(onChange);
7446
+ React$1.useEffect(() => {
7390
7447
  onChangeRef.current = onChange;
7391
7448
  }, [onChange]);
7392
- const [internalFocusMode, setInternalFocusMode] = React.useState("normal");
7449
+ const [internalFocusMode, setInternalFocusMode] = React$1.useState("normal");
7393
7450
  const focusMode = controlledFocusMode ?? internalFocusMode;
7394
7451
  const setFocusMode = (mode) => {
7395
7452
  if (onFocusModeChange) onFocusModeChange(mode);
7396
7453
  else setInternalFocusMode(mode);
7397
7454
  };
7398
- const [mediaPickerOpen, setMediaPickerOpen] = React.useState(false);
7399
- const [pluginBlockModal, setPluginBlockModal] = React.useState(null);
7400
- const [pluginBlockInitialValues, setPluginBlockInitialValues] = React.useState(void 0);
7455
+ const [mediaPickerOpen, setMediaPickerOpen] = React$1.useState(false);
7456
+ const [pluginBlockModal, setPluginBlockModal] = React$1.useState(null);
7457
+ const [pluginBlockInitialValues, setPluginBlockInitialValues] = React$1.useState(void 0);
7401
7458
  /** When editing an existing block, store the node position for updateAttributes */
7402
- const editingBlockPosRef = React.useRef(null);
7403
- const [sectionPickerOpen, setSectionPickerOpen] = React.useState(false);
7404
- const [slashMenuState, setSlashMenuState] = React.useState({
7459
+ const editingBlockPosRef = React$1.useRef(null);
7460
+ const [sectionPickerOpen, setSectionPickerOpen] = React$1.useState(false);
7461
+ const [slashMenuState, setSlashMenuState] = React$1.useState({
7405
7462
  isOpen: false,
7406
7463
  items: [],
7407
7464
  selectedIndex: 0,
7408
7465
  clientRect: null,
7409
7466
  range: null
7410
7467
  });
7411
- const slashMenuStateRef = React.useRef(slashMenuState);
7412
- React.useEffect(() => {
7468
+ const slashMenuStateRef = React$1.useRef(slashMenuState);
7469
+ React$1.useEffect(() => {
7413
7470
  slashMenuStateRef.current = slashMenuState;
7414
7471
  }, [slashMenuState]);
7415
- const slashCommands = React.useMemo(() => {
7472
+ const slashCommands = React$1.useMemo(() => {
7416
7473
  const cmds = [...defaultSlashCommands];
7417
7474
  cmds.push({
7418
7475
  id: "image",
@@ -7486,7 +7543,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7486
7543
  });
7487
7544
  return cmds;
7488
7545
  }, [pluginBlocks, _t4]);
7489
- const filterCommandsRef = React.useRef((_q) => []);
7546
+ const filterCommandsRef = React$1.useRef((_q) => []);
7490
7547
  filterCommandsRef.current = (query) => {
7491
7548
  if (!query) return slashCommands;
7492
7549
  const searchText = query.toLowerCase();
@@ -7500,9 +7557,9 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7500
7557
  }
7501
7558
  return [...titleMatches, ...otherMatches];
7502
7559
  };
7503
- const initialContent = React.useMemo(() => portableTextToProsemirror(value || []), []);
7560
+ const initialContent = React$1.useMemo(() => portableTextToProsemirror(value || []), []);
7504
7561
  const editor = useEditor({
7505
- extensions: React.useMemo(() => [
7562
+ extensions: React$1.useMemo(() => [
7506
7563
  StarterKit.configure({
7507
7564
  heading: { levels: [
7508
7565
  1,
@@ -7546,7 +7603,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7546
7603
  content: initialContent,
7547
7604
  editable,
7548
7605
  immediatelyRender: true,
7549
- editorProps: React.useMemo(() => ({ attributes: {
7606
+ editorProps: React$1.useMemo(() => ({ attributes: {
7550
7607
  class: "prose prose-sm sm:prose-base dark:prose-invert max-w-none focus:outline-none min-h-[200px] p-4",
7551
7608
  dir: "auto"
7552
7609
  } }), []),
@@ -7555,7 +7612,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7555
7612
  if (cb) cb(prosemirrorToPortableText(updatedEditor.getJSON()));
7556
7613
  }
7557
7614
  });
7558
- React.useEffect(() => {
7615
+ React$1.useEffect(() => {
7559
7616
  if (editor && onEditorReady) {
7560
7617
  onEditorReady(editor);
7561
7618
  return () => {
@@ -7563,10 +7620,10 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7563
7620
  };
7564
7621
  }
7565
7622
  }, [editor, onEditorReady]);
7566
- React.useEffect(() => {
7623
+ React$1.useEffect(() => {
7567
7624
  if (editor) registerPluginBlocks(editor, pluginBlocks);
7568
7625
  }, [editor, pluginBlocks]);
7569
- React.useEffect(() => {
7626
+ React$1.useEffect(() => {
7570
7627
  if (!editor) return;
7571
7628
  const storage = editor.storage.pluginBlock;
7572
7629
  if (!storage) return;
@@ -7584,11 +7641,11 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7584
7641
  storage.onEditBlock = null;
7585
7642
  };
7586
7643
  }, [editor, pluginBlocks]);
7587
- const onBlockSidebarOpenRef = React.useRef(onBlockSidebarOpen);
7644
+ const onBlockSidebarOpenRef = React$1.useRef(onBlockSidebarOpen);
7588
7645
  onBlockSidebarOpenRef.current = onBlockSidebarOpen;
7589
- const onBlockSidebarCloseRef = React.useRef(onBlockSidebarClose);
7646
+ const onBlockSidebarCloseRef = React$1.useRef(onBlockSidebarClose);
7590
7647
  onBlockSidebarCloseRef.current = onBlockSidebarClose;
7591
- React.useEffect(() => {
7648
+ React$1.useEffect(() => {
7592
7649
  if (!editor) return;
7593
7650
  const storage = editor.storage.image;
7594
7651
  if (!storage) return;
@@ -7603,7 +7660,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7603
7660
  storage.onCloseBlockSidebar = null;
7604
7661
  };
7605
7662
  }, [editor]);
7606
- const handleImageSelect = React.useCallback((item) => {
7663
+ const handleImageSelect = React$1.useCallback((item) => {
7607
7664
  if (editor) editor.chain().focus().setImage({
7608
7665
  src: item.url,
7609
7666
  alt: item.alt || item.filename,
@@ -7614,7 +7671,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7614
7671
  }).run();
7615
7672
  setMediaPickerOpen(false);
7616
7673
  }, [editor]);
7617
- const handlePluginBlockInsert = React.useCallback((values) => {
7674
+ const handlePluginBlockInsert = React$1.useCallback((values) => {
7618
7675
  if (!editor || !pluginBlockModal) return;
7619
7676
  const { id, ...data } = values;
7620
7677
  const editPos = editingBlockPosRef.current;
@@ -7641,7 +7698,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7641
7698
  setPluginBlockInitialValues(void 0);
7642
7699
  editingBlockPosRef.current = null;
7643
7700
  }, [editor, pluginBlockModal]);
7644
- const handleSlashCommand = React.useCallback((item) => {
7701
+ const handleSlashCommand = React$1.useCallback((item) => {
7645
7702
  if (editor && slashMenuState.range) {
7646
7703
  item.command({
7647
7704
  editor,
@@ -7653,7 +7710,7 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7653
7710
  }));
7654
7711
  }
7655
7712
  }, [editor, slashMenuState.range]);
7656
- const handleSectionSelect = React.useCallback((section) => {
7713
+ const handleSectionSelect = React$1.useCallback((section) => {
7657
7714
  if (!editor || !section.content || section.content.length === 0) return;
7658
7715
  const { content: prosemirrorContent } = portableTextToProsemirror(Array.isArray(section.content) ? section.content : []);
7659
7716
  editor.chain().focus().insertContent(prosemirrorContent).run();
@@ -7722,10 +7779,10 @@ function PortableTextEditor({ value, onChange, placeholder = "Start writing...",
7722
7779
  * Shows inline formatting options and link editing
7723
7780
  */
7724
7781
  function EditorBubbleMenu({ editor }) {
7725
- const [showLinkInput, setShowLinkInput] = React.useState(false);
7726
- const [linkUrl, setLinkUrl] = React.useState("");
7727
- const inputRef = React.useRef(null);
7728
- React.useEffect(() => {
7782
+ const [showLinkInput, setShowLinkInput] = React$1.useState(false);
7783
+ const [linkUrl, setLinkUrl] = React$1.useState("");
7784
+ const inputRef = React$1.useRef(null);
7785
+ React$1.useEffect(() => {
7729
7786
  if (showLinkInput) {
7730
7787
  setLinkUrl(editor.getAttributes("link").href || "");
7731
7788
  setTimeout(() => inputRef.current?.focus(), 0);
@@ -7854,11 +7911,11 @@ function BubbleButton({ onClick, active, title, children }) {
7854
7911
  * Arrow keys move focus between buttons, Home/End jump to first/last.
7855
7912
  */
7856
7913
  function EditorToolbar({ editor, focusMode, onFocusModeChange }) {
7857
- const [mediaPickerOpen, setMediaPickerOpen] = React.useState(false);
7858
- const [showLinkPopover, setShowLinkPopover] = React.useState(false);
7859
- const [linkUrl, setLinkUrl] = React.useState("");
7860
- const toolbarRef = React.useRef(null);
7861
- const linkInputRef = React.useRef(null);
7914
+ const [mediaPickerOpen, setMediaPickerOpen] = React$1.useState(false);
7915
+ const [showLinkPopover, setShowLinkPopover] = React$1.useState(false);
7916
+ const [linkUrl, setLinkUrl] = React$1.useState("");
7917
+ const toolbarRef = React$1.useRef(null);
7918
+ const linkInputRef = React$1.useRef(null);
7862
7919
  const editorState = useEditorState({
7863
7920
  editor,
7864
7921
  selector: (ctx) => ({
@@ -7882,7 +7939,7 @@ function EditorToolbar({ editor, focusMode, onFocusModeChange }) {
7882
7939
  canRedo: ctx.editor.can().redo()
7883
7940
  })
7884
7941
  });
7885
- React.useEffect(() => {
7942
+ React$1.useEffect(() => {
7886
7943
  if (showLinkPopover) {
7887
7944
  setLinkUrl(editor.getAttributes("link").href || "");
7888
7945
  setTimeout(() => linkInputRef.current?.focus(), 0);
@@ -7909,7 +7966,7 @@ function EditorToolbar({ editor, focusMode, onFocusModeChange }) {
7909
7966
  editor.commands.focus();
7910
7967
  }
7911
7968
  };
7912
- const handleImageSelect = React.useCallback((item) => {
7969
+ const handleImageSelect = React$1.useCallback((item) => {
7913
7970
  editor.chain().focus().setImage({
7914
7971
  src: item.url,
7915
7972
  alt: item.alt || item.filename,
@@ -7918,7 +7975,7 @@ function EditorToolbar({ editor, focusMode, onFocusModeChange }) {
7918
7975
  height: item.height
7919
7976
  }).run();
7920
7977
  }, [editor]);
7921
- const handleKeyDown = React.useCallback((e) => {
7978
+ const handleKeyDown = React$1.useCallback((e) => {
7922
7979
  const toolbar = toolbarRef.current;
7923
7980
  if (!toolbar) return;
7924
7981
  const buttons = [...toolbar.querySelectorAll("button:not([disabled]), [role=\"button\"]:not([disabled])")];
@@ -8316,9 +8373,9 @@ function formatFullDate(dateString) {
8316
8373
  */
8317
8374
  function RevisionHistory({ collection, entryId, onRestored }) {
8318
8375
  const { _: _t } = useLingui();
8319
- const [isExpanded, setIsExpanded] = React.useState(false);
8320
- const [selectedRevision, setSelectedRevision] = React.useState(null);
8321
- const [restoreTarget, setRestoreTarget] = React.useState(null);
8376
+ const [isExpanded, setIsExpanded] = React$1.useState(false);
8377
+ const [selectedRevision, setSelectedRevision] = React$1.useState(null);
8378
+ const [restoreTarget, setRestoreTarget] = React$1.useState(null);
8322
8379
  const queryClient = useQueryClient();
8323
8380
  const toastManager = Toast.useToastManager();
8324
8381
  const { data, isLoading, error } = useQuery({
@@ -8526,8 +8583,8 @@ function RevisionItem({ revision, compareRevision, isLatest, isRestoring, isSele
8526
8583
  }
8527
8584
  function RevisionDiffView({ older, newer }) {
8528
8585
  const { _: _t3 } = useLingui();
8529
- const [showUnchanged, setShowUnchanged] = React.useState(false);
8530
- const diffs = React.useMemo(() => computeFieldDiff(older, newer), [older, newer]);
8586
+ const [showUnchanged, setShowUnchanged] = React$1.useState(false);
8587
+ const diffs = React$1.useMemo(() => computeFieldDiff(older, newer), [older, newer]);
8531
8588
  const changedCount = diffs.filter((d) => d.kind !== "unchanged").length;
8532
8589
  const unchangedCount = diffs.length - changedCount;
8533
8590
  if (diffs.length === 0) return /* @__PURE__ */ jsx("div", {
@@ -8698,7 +8755,7 @@ function SaveButton({ isDirty, isSaving, className, disabled, ...props }) {
8698
8755
  */
8699
8756
  function SeoImageField({ seo, onChange }) {
8700
8757
  const { _: _t } = useLingui();
8701
- const [pickerOpen, setPickerOpen] = React.useState(false);
8758
+ const [pickerOpen, setPickerOpen] = React$1.useState(false);
8702
8759
  const imageUrl = seo?.image || null;
8703
8760
  const handleSelect = (item) => {
8704
8761
  onChange({ image: !item.provider || item.provider === "local" ? `/_emdash/api/media/file/${item.storageKey || item.id}` : item.url });
@@ -8805,33 +8862,33 @@ function serializeDraft(draft) {
8805
8862
  }
8806
8863
  function SeoPanel({ contentKey, seo, onChange }) {
8807
8864
  const { _: _t } = useLingui();
8808
- const propDraft = React.useMemo(() => toDraft(seo), [seo]);
8809
- const propSnapshot = React.useMemo(() => serializeDraft(propDraft), [propDraft]);
8810
- const [draft, setDraft] = React.useState(propDraft);
8811
- const currentDraftRef = React.useRef(draft);
8865
+ const propDraft = React$1.useMemo(() => toDraft(seo), [seo]);
8866
+ const propSnapshot = React$1.useMemo(() => serializeDraft(propDraft), [propDraft]);
8867
+ const [draft, setDraft] = React$1.useState(propDraft);
8868
+ const currentDraftRef = React$1.useRef(draft);
8812
8869
  currentDraftRef.current = draft;
8813
- const lastPropSnapshotRef = React.useRef(propSnapshot);
8814
- const lastEmittedSnapshotRef = React.useRef(propSnapshot);
8815
- const activeContentKeyRef = React.useRef(contentKey);
8816
- const activeOnChangeRef = React.useRef(onChange);
8817
- const pendingTextFlushTimerRef = React.useRef(null);
8818
- const emitChange = React.useCallback((nextDraft) => {
8870
+ const lastPropSnapshotRef = React$1.useRef(propSnapshot);
8871
+ const lastEmittedSnapshotRef = React$1.useRef(propSnapshot);
8872
+ const activeContentKeyRef = React$1.useRef(contentKey);
8873
+ const activeOnChangeRef = React$1.useRef(onChange);
8874
+ const pendingTextFlushTimerRef = React$1.useRef(null);
8875
+ const emitChange = React$1.useCallback((nextDraft) => {
8819
8876
  const nextSnapshot = serializeDraft(nextDraft);
8820
8877
  if (nextSnapshot === lastEmittedSnapshotRef.current) return;
8821
8878
  lastEmittedSnapshotRef.current = nextSnapshot;
8822
8879
  activeOnChangeRef.current(toInput(nextDraft));
8823
8880
  }, []);
8824
- const clearPendingTextFlush = React.useCallback(() => {
8881
+ const clearPendingTextFlush = React$1.useCallback(() => {
8825
8882
  if (pendingTextFlushTimerRef.current) {
8826
8883
  clearTimeout(pendingTextFlushTimerRef.current);
8827
8884
  pendingTextFlushTimerRef.current = null;
8828
8885
  }
8829
8886
  }, []);
8830
- const flushPendingDraft = React.useCallback(() => {
8887
+ const flushPendingDraft = React$1.useCallback(() => {
8831
8888
  clearPendingTextFlush();
8832
8889
  emitChange(currentDraftRef.current);
8833
8890
  }, [clearPendingTextFlush, emitChange]);
8834
- React.useEffect(() => {
8891
+ React$1.useEffect(() => {
8835
8892
  if (activeContentKeyRef.current === contentKey) {
8836
8893
  activeOnChangeRef.current = onChange;
8837
8894
  return;
@@ -8850,12 +8907,12 @@ function SeoPanel({ contentKey, seo, onChange }) {
8850
8907
  propDraft,
8851
8908
  propSnapshot
8852
8909
  ]);
8853
- React.useEffect(() => {
8910
+ React$1.useEffect(() => {
8854
8911
  return () => {
8855
8912
  flushPendingDraft();
8856
8913
  };
8857
8914
  }, [flushPendingDraft]);
8858
- React.useEffect(() => {
8915
+ React$1.useEffect(() => {
8859
8916
  const previousPropSnapshot = lastPropSnapshotRef.current;
8860
8917
  if (propSnapshot === previousPropSnapshot) return;
8861
8918
  const currentDraftSnapshot = serializeDraft(currentDraftRef.current);
@@ -8866,7 +8923,7 @@ function SeoPanel({ contentKey, seo, onChange }) {
8866
8923
  }
8867
8924
  lastPropSnapshotRef.current = propSnapshot;
8868
8925
  }, [propDraft, propSnapshot]);
8869
- React.useEffect(() => {
8926
+ React$1.useEffect(() => {
8870
8927
  clearPendingTextFlush();
8871
8928
  if (serializeDraft(currentDraftRef.current) === lastEmittedSnapshotRef.current) return;
8872
8929
  pendingTextFlushTimerRef.current = setTimeout(() => {
@@ -9090,10 +9147,10 @@ function CategoryCheckboxTree({ term, level = 0, selectedIds, onToggle }) {
9090
9147
  */
9091
9148
  function TagInput({ terms, selectedIds, onAdd, onRemove, onCreate, isCreating, label }) {
9092
9149
  const { _: _t } = useLingui();
9093
- const [input, setInput] = React.useState("");
9150
+ const [input, setInput] = React$1.useState("");
9094
9151
  const selectedTerms = terms.filter((term) => selectedIds.has(term.id));
9095
9152
  const trimmedInput = input.trim();
9096
- const suggestions = React.useMemo(() => {
9153
+ const suggestions = React$1.useMemo(() => {
9097
9154
  if (!trimmedInput) return [];
9098
9155
  return terms.filter((term) => !selectedIds.has(term.id) && termMatches(term, trimmedInput)).slice(0, 5);
9099
9156
  }, [
@@ -9101,7 +9158,7 @@ function TagInput({ terms, selectedIds, onAdd, onRemove, onCreate, isCreating, l
9101
9158
  terms,
9102
9159
  selectedIds
9103
9160
  ]);
9104
- const hasExactMatch = React.useMemo(() => {
9161
+ const hasExactMatch = React$1.useMemo(() => {
9105
9162
  if (!trimmedInput) return false;
9106
9163
  return terms.some((term) => termExactMatches(term, trimmedInput));
9107
9164
  }, [trimmedInput, terms]);
@@ -9181,8 +9238,8 @@ function TaxonomySection({ taxonomy, collection, entryId, onChange }) {
9181
9238
  const { _: _t2 } = useLingui();
9182
9239
  const queryClient = useQueryClient();
9183
9240
  const toastManager = Toast.useToastManager();
9184
- const [newCategoryLabel, setNewCategoryLabel] = React.useState("");
9185
- const [showCategoryInput, setShowCategoryInput] = React.useState(false);
9241
+ const [newCategoryLabel, setNewCategoryLabel] = React$1.useState("");
9242
+ const [showCategoryInput, setShowCategoryInput] = React$1.useState(false);
9186
9243
  const { data: terms = [] } = useQuery({
9187
9244
  queryKey: ["taxonomy-terms", taxonomy.name],
9188
9245
  queryFn: () => fetchTerms$1(taxonomy.name)
@@ -9250,8 +9307,8 @@ function TaxonomySection({ taxonomy, collection, entryId, onChange }) {
9250
9307
  setShowCategoryInput(false);
9251
9308
  }
9252
9309
  });
9253
- const [selectedIds, setSelectedIds] = React.useState(/* @__PURE__ */ new Set());
9254
- React.useEffect(() => {
9310
+ const [selectedIds, setSelectedIds] = React$1.useState(/* @__PURE__ */ new Set());
9311
+ React$1.useEffect(() => {
9255
9312
  setSelectedIds(new Set(entryTerms.map((term) => term.id)));
9256
9313
  }, [entryTerms]);
9257
9314
  const handleToggle = (termId) => {
@@ -9387,6 +9444,81 @@ function TaxonomySidebar({ collection, entryId, onChange }) {
9387
9444
  });
9388
9445
  }
9389
9446
 
9447
+ //#endregion
9448
+ //#region src/components/TranslationsPanel.tsx
9449
+ /**
9450
+ * Shared "Translations" sidebar panel. Matches the look of the translations
9451
+ * section in ContentEditor — reused across content edit, menu edit and
9452
+ * taxonomy term edit so the admin shows one consistent affordance.
9453
+ */
9454
+ function TranslationsPanel({ title, locales, defaultLocale, currentLocale, translations, onOpen, onCreate, pendingLocale }) {
9455
+ const { _: _t } = useLingui();
9456
+ const byLocale = React$1.useMemo(() => new Map(translations.map((tr) => [tr.locale, tr])), [translations]);
9457
+ return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
9458
+ className: "mb-4 font-semibold",
9459
+ children: title ?? _t({
9460
+ id: "kAy5R9",
9461
+ message: "Translations"
9462
+ })
9463
+ }), /* @__PURE__ */ jsx("div", {
9464
+ className: "space-y-2",
9465
+ children: locales.map((locale) => {
9466
+ const translation = byLocale.get(locale);
9467
+ const isCurrent = locale === currentLocale;
9468
+ return /* @__PURE__ */ jsxs("div", {
9469
+ className: cn("flex items-center justify-between rounded-md px-3 py-2 text-sm", isCurrent ? "bg-kumo-brand/10 font-medium" : translation ? "hover:bg-kumo-tint/50" : "text-kumo-subtle"),
9470
+ children: [/* @__PURE__ */ jsxs("div", {
9471
+ className: "flex items-center gap-2",
9472
+ children: [
9473
+ /* @__PURE__ */ jsx("span", {
9474
+ className: "text-xs font-semibold uppercase",
9475
+ children: locale
9476
+ }),
9477
+ locale === defaultLocale && /* @__PURE__ */ jsx("span", {
9478
+ className: "text-[10px] text-kumo-subtle",
9479
+ children: _t({
9480
+ id: "FozKV6",
9481
+ message: " (default)"
9482
+ })
9483
+ }),
9484
+ isCurrent && /* @__PURE__ */ jsx("span", {
9485
+ className: "text-[10px] text-kumo-brand",
9486
+ children: _t({
9487
+ id: "7vEC4B",
9488
+ message: "current"
9489
+ })
9490
+ })
9491
+ ]
9492
+ }), isCurrent ? null : translation && onOpen ? /* @__PURE__ */ jsx(Button, {
9493
+ type: "button",
9494
+ variant: "ghost",
9495
+ size: "sm",
9496
+ className: "h-auto px-2 py-1 text-xs",
9497
+ onClick: () => onOpen(translation),
9498
+ children: _t({
9499
+ id: "ePK91l",
9500
+ message: "Edit"
9501
+ })
9502
+ }) : !translation && onCreate ? /* @__PURE__ */ jsx(Button, {
9503
+ type: "button",
9504
+ variant: "ghost",
9505
+ size: "sm",
9506
+ className: "h-auto px-2 py-1 text-xs",
9507
+ disabled: pendingLocale === locale,
9508
+ onClick: () => onCreate(locale),
9509
+ children: pendingLocale === locale ? _t({
9510
+ id: "wvgKtH",
9511
+ message: "Translating..."
9512
+ }) : _t({
9513
+ id: "pi8x/S",
9514
+ message: "Translate"
9515
+ })
9516
+ }) : null]
9517
+ }, locale);
9518
+ })
9519
+ })] });
9520
+ }
9521
+
9390
9522
  //#endregion
9391
9523
  //#region src/components/ContentEditor.tsx
9392
9524
  /** Autosave debounce delay in milliseconds */
@@ -9409,29 +9541,30 @@ function formatScheduledDate(dateStr) {
9409
9541
  */
9410
9542
  function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSaving, onSave, onAutosave, isAutosaving, lastAutosaveAt, onPublish, onUnpublish, onDiscardDraft, onSchedule, onUnschedule, isScheduling, supportsDrafts = false, supportsRevisions = false, supportsPreview = false, currentUser, users, onAuthorChange, availableBylines, selectedBylines, onBylinesChange, onQuickCreateByline, onQuickEditByline, onDelete, isDeleting, i18n, translations, onTranslate, pluginBlocks, hasSeo = false, onSeoChange, manifest }) {
9411
9543
  const { _: _t } = useLingui();
9412
- const [formData, setFormData] = React.useState(item?.data || {});
9413
- const [slug, setSlug] = React.useState(item?.slug || "");
9414
- const [slugTouched, setSlugTouched] = React.useState(!!item?.slug);
9415
- const [status, setStatus] = React.useState(item?.status || "draft");
9416
- const [internalBylines, setInternalBylines] = React.useState(item?.bylines?.map((entry) => ({
9544
+ const navigate = useNavigate$1();
9545
+ const [formData, setFormData] = React$1.useState(item?.data || {});
9546
+ const [slug, setSlug] = React$1.useState(item?.slug || "");
9547
+ const [slugTouched, setSlugTouched] = React$1.useState(!!item?.slug);
9548
+ const [status, setStatus] = React$1.useState(item?.status || "draft");
9549
+ const [internalBylines, setInternalBylines] = React$1.useState(item?.bylines?.map((entry) => ({
9417
9550
  bylineId: entry.byline.id,
9418
9551
  roleLabel: entry.roleLabel
9419
9552
  })) ?? []);
9420
- const [portableTextEditor, setPortableTextEditor] = React.useState(null);
9421
- const [blockSidebarPanel, setBlockSidebarPanel] = React.useState(null);
9422
- const handleBlockSidebarOpen = React.useCallback((panel) => {
9553
+ const [portableTextEditor, setPortableTextEditor] = React$1.useState(null);
9554
+ const [blockSidebarPanel, setBlockSidebarPanel] = React$1.useState(null);
9555
+ const handleBlockSidebarOpen = React$1.useCallback((panel) => {
9423
9556
  setBlockSidebarPanel(panel);
9424
9557
  }, []);
9425
- const handleBlockSidebarClose = React.useCallback(() => {
9558
+ const handleBlockSidebarClose = React$1.useCallback(() => {
9426
9559
  setBlockSidebarPanel((prev) => {
9427
9560
  prev?.onClose();
9428
9561
  return null;
9429
9562
  });
9430
9563
  }, []);
9431
- const handleSeoChange = React.useCallback((seo) => {
9564
+ const handleSeoChange = React$1.useCallback((seo) => {
9432
9565
  onSeoChange?.(seo);
9433
9566
  }, [onSeoChange]);
9434
- const [lastSavedData, setLastSavedData] = React.useState(serializeEditorState({
9567
+ const [lastSavedData, setLastSavedData] = React$1.useState(serializeEditorState({
9435
9568
  data: item?.data || {},
9436
9569
  slug: item?.slug || "",
9437
9570
  bylines: item?.bylines?.map((entry) => ({
@@ -9439,8 +9572,8 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9439
9572
  roleLabel: entry.roleLabel
9440
9573
  })) ?? []
9441
9574
  }));
9442
- const pendingAutosaveStateRef = React.useRef(null);
9443
- const [previousItemId, setPreviousItemId] = React.useState(item?.id ?? null);
9575
+ const pendingAutosaveStateRef = React$1.useRef(null);
9576
+ const [previousItemId, setPreviousItemId] = React$1.useState(item?.id ?? null);
9444
9577
  if (item && item.id !== previousItemId) {
9445
9578
  setPreviousItemId(item.id);
9446
9579
  setFormData(item.data);
@@ -9459,8 +9592,8 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9459
9592
  }));
9460
9593
  pendingAutosaveStateRef.current = null;
9461
9594
  }
9462
- const itemDataString = React.useMemo(() => item ? JSON.stringify(item.data) : "", [item?.data]);
9463
- React.useEffect(() => {
9595
+ const itemDataString = React$1.useMemo(() => item ? JSON.stringify(item.data) : "", [item?.data]);
9596
+ React$1.useEffect(() => {
9464
9597
  if (item) {
9465
9598
  setFormData(item.data);
9466
9599
  setSlug(item.slug || "");
@@ -9487,7 +9620,7 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9487
9620
  item?.status
9488
9621
  ]);
9489
9622
  const activeBylines = isNew ? selectedBylines ?? [] : internalBylines;
9490
- const handleBylinesChange = React.useCallback((next) => {
9623
+ const handleBylinesChange = React$1.useCallback((next) => {
9491
9624
  if (isNew) {
9492
9625
  onBylinesChange?.(next);
9493
9626
  return;
@@ -9495,7 +9628,7 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9495
9628
  setInternalBylines(next);
9496
9629
  onBylinesChange?.(next);
9497
9630
  }, [isNew, onBylinesChange]);
9498
- const currentData = React.useMemo(() => serializeEditorState({
9631
+ const currentData = React$1.useMemo(() => serializeEditorState({
9499
9632
  data: formData,
9500
9633
  slug,
9501
9634
  bylines: activeBylines
@@ -9505,24 +9638,24 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9505
9638
  activeBylines
9506
9639
  ]);
9507
9640
  const isDirty = isNew || currentData !== lastSavedData;
9508
- const autosaveTimeoutRef = React.useRef(null);
9509
- const formDataRef = React.useRef(formData);
9641
+ const autosaveTimeoutRef = React$1.useRef(null);
9642
+ const formDataRef = React$1.useRef(formData);
9510
9643
  formDataRef.current = formData;
9511
- const slugRef = React.useRef(slug);
9644
+ const slugRef = React$1.useRef(slug);
9512
9645
  slugRef.current = slug;
9513
- React.useEffect(() => {
9646
+ React$1.useEffect(() => {
9514
9647
  if (!lastAutosaveAt || !pendingAutosaveStateRef.current) return;
9515
9648
  setLastSavedData(pendingAutosaveStateRef.current);
9516
9649
  pendingAutosaveStateRef.current = null;
9517
9650
  }, [lastAutosaveAt]);
9518
- const hasInvalidUrls = React.useCallback((data) => {
9651
+ const hasInvalidUrls = React$1.useCallback((data) => {
9519
9652
  for (const [name, field] of Object.entries(fields)) if (field.kind === "url") {
9520
9653
  const val = typeof data[name] === "string" ? data[name].trim() : "";
9521
9654
  if (val && !isValidUrl(val)) return true;
9522
9655
  }
9523
9656
  return false;
9524
9657
  }, [fields]);
9525
- React.useEffect(() => {
9658
+ React$1.useEffect(() => {
9526
9659
  if (isNew || !onAutosave || !item?.id) return;
9527
9660
  if (!isDirty || isSaving || isAutosaving) return;
9528
9661
  if (autosaveTimeoutRef.current) clearTimeout(autosaveTimeoutRef.current);
@@ -9567,7 +9700,7 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9567
9700
  bylines: activeBylines
9568
9701
  });
9569
9702
  };
9570
- const [isLoadingPreview, setIsLoadingPreview] = React.useState(false);
9703
+ const [isLoadingPreview, setIsLoadingPreview] = React$1.useState(false);
9571
9704
  const urlPattern = manifest?.collections[collection]?.urlPattern;
9572
9705
  const handlePreview = async () => {
9573
9706
  if (!item?.id) return;
@@ -9582,7 +9715,7 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9582
9715
  setIsLoadingPreview(false);
9583
9716
  }
9584
9717
  };
9585
- const handleFieldChange = React.useCallback((name, value) => {
9718
+ const handleFieldChange = React$1.useCallback((name, value) => {
9586
9719
  setFormData((prev) => ({
9587
9720
  ...prev,
9588
9721
  [name]: value
@@ -9599,10 +9732,10 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
9599
9732
  const isLive = draftStatus === "published" || draftStatus === "published_with_changes";
9600
9733
  const hasSchedule = Boolean(item?.scheduledAt);
9601
9734
  const canSchedule = !isNew && !hasSchedule && Boolean(onSchedule) && (!isPublished || hasPendingChanges);
9602
- const [scheduleDate, setScheduleDate] = React.useState("");
9603
- const [showScheduler, setShowScheduler] = React.useState(false);
9604
- const [isDistractionFree, setIsDistractionFree] = React.useState(false);
9605
- React.useEffect(() => {
9735
+ const [scheduleDate, setScheduleDate] = React$1.useState("");
9736
+ const [showScheduler, setShowScheduler] = React$1.useState(false);
9737
+ const [isDistractionFree, setIsDistractionFree] = React$1.useState(false);
9738
+ React$1.useEffect(() => {
9606
9739
  if (!isDistractionFree) return;
9607
9740
  const handleKeyDown = (e) => {
9608
9741
  if (e.key === "Escape") {
@@ -10111,68 +10244,22 @@ function ContentEditor({ collection, collectionLabel, item, fields, isNew, isSav
10111
10244
  onQuickEdit: onQuickEditByline
10112
10245
  })]
10113
10246
  }),
10114
- i18n && item && !isNew && /* @__PURE__ */ jsxs("div", {
10247
+ i18n && item && !isNew && /* @__PURE__ */ jsx("div", {
10115
10248
  className: "p-4 border-t",
10116
- children: [/* @__PURE__ */ jsx("h3", {
10117
- className: "mb-4 font-semibold",
10118
- children: _t({
10119
- id: "kAy5R9",
10120
- message: "Translations"
10121
- })
10122
- }), /* @__PURE__ */ jsx("div", {
10123
- className: "space-y-2",
10124
- children: i18n.locales.map((locale) => {
10125
- const translation = translations?.find((tr) => tr.locale === locale);
10126
- const isCurrent = locale === item.locale;
10127
- return /* @__PURE__ */ jsxs("div", {
10128
- className: cn("flex items-center justify-between rounded-md px-3 py-2 text-sm", isCurrent ? "bg-kumo-brand/10 font-medium" : translation ? "hover:bg-kumo-tint/50" : "text-kumo-subtle"),
10129
- children: [/* @__PURE__ */ jsxs("div", {
10130
- className: "flex items-center gap-2",
10131
- children: [
10132
- /* @__PURE__ */ jsx("span", {
10133
- className: "text-xs font-semibold uppercase",
10134
- children: locale
10135
- }),
10136
- locale === i18n.defaultLocale && /* @__PURE__ */ jsx("span", {
10137
- className: "text-[10px] text-kumo-subtle",
10138
- children: _t({
10139
- id: "FozKV6",
10140
- message: " (default)"
10141
- })
10142
- }),
10143
- isCurrent && /* @__PURE__ */ jsx("span", {
10144
- className: "text-[10px] text-kumo-brand",
10145
- children: _t({
10146
- id: "7vEC4B",
10147
- message: "current"
10148
- })
10149
- })
10150
- ]
10151
- }), translation && !isCurrent ? /* @__PURE__ */ jsx(Link$1, {
10152
- to: "/content/$collection/$id",
10153
- params: {
10154
- collection,
10155
- id: translation.id
10156
- },
10157
- className: "text-xs text-kumo-brand hover:underline",
10158
- children: _t({
10159
- id: "ePK91l",
10160
- message: "Edit"
10161
- })
10162
- }) : !translation && onTranslate ? /* @__PURE__ */ jsx(Button, {
10163
- type: "button",
10164
- variant: "ghost",
10165
- size: "sm",
10166
- className: "h-auto px-2 py-1 text-xs",
10167
- onClick: () => onTranslate(locale),
10168
- children: _t({
10169
- id: "pi8x/S",
10170
- message: "Translate"
10171
- })
10172
- }) : null]
10173
- }, locale);
10174
- })
10175
- })]
10249
+ children: /* @__PURE__ */ jsx(TranslationsPanel, {
10250
+ locales: i18n.locales,
10251
+ defaultLocale: i18n.defaultLocale,
10252
+ currentLocale: item.locale ?? void 0,
10253
+ translations: translations ?? [],
10254
+ onOpen: (tr) => navigate({
10255
+ to: "/content/$collection/$id",
10256
+ params: {
10257
+ collection,
10258
+ id: tr.id
10259
+ }
10260
+ }),
10261
+ onCreate: onTranslate
10262
+ })
10176
10263
  }),
10177
10264
  item && /* @__PURE__ */ jsx("div", {
10178
10265
  className: "p-4 border-t",
@@ -10221,7 +10308,7 @@ function FieldRenderer({ name, field, value, onChange, onEditorReady, minimal, p
10221
10308
  const label = field.label || name.charAt(0).toUpperCase() + name.slice(1);
10222
10309
  const id = `field-${name}`;
10223
10310
  const labelClass = minimal ? "text-kumo-subtle/50 text-xs font-normal" : void 0;
10224
- const handleChange = React.useCallback((v) => onChange(name, v), [onChange, name]);
10311
+ const handleChange = React$1.useCallback((v) => onChange(name, v), [onChange, name]);
10225
10312
  if (field.widget) {
10226
10313
  const sepIdx = field.widget.indexOf(":");
10227
10314
  if (sepIdx <= 0) console.warn(`[emdash] Field "${name}" has widget "${field.widget}" but it should use the format "pluginId:widgetName". Falling back to default editor.`);
@@ -10380,6 +10467,13 @@ function FieldRenderer({ name, field, value, onChange, onEditorReady, minimal, p
10380
10467
  required: field.required
10381
10468
  });
10382
10469
  }
10470
+ case "file": return /* @__PURE__ */ jsx(FileFieldRenderer, {
10471
+ id,
10472
+ label,
10473
+ value: value != null && typeof value === "object" ? value : void 0,
10474
+ onChange: handleChange,
10475
+ required: field.required
10476
+ });
10383
10477
  case "repeater": {
10384
10478
  const validation = field.validation;
10385
10479
  const subFields = validation?.subFields ?? [];
@@ -10437,7 +10531,7 @@ function isValidUrl(val) {
10437
10531
  */
10438
10532
  function UrlFieldEditor({ label, labelClass, id, value, onChange, required, placeholder }) {
10439
10533
  const { _: _t3 } = useLingui();
10440
- const [error, setError] = React.useState(null);
10534
+ const [error, setError] = React$1.useState(null);
10441
10535
  const handleBlur = (e) => {
10442
10536
  const val = e.target.value.trim();
10443
10537
  if (!val) {
@@ -10475,9 +10569,9 @@ function UrlFieldEditor({ label, labelClass, id, value, onChange, required, plac
10475
10569
  */
10476
10570
  function JsonFieldEditor({ label, id, value, onChange, required }) {
10477
10571
  const { _: _t4 } = useLingui();
10478
- const [text, setText] = React.useState(value);
10479
- const [error, setError] = React.useState(null);
10480
- React.useEffect(() => {
10572
+ const [text, setText] = React$1.useState(value);
10573
+ const [error, setError] = React$1.useState(null);
10574
+ React$1.useEffect(() => {
10481
10575
  setText(value);
10482
10576
  setError(null);
10483
10577
  }, [value]);
@@ -10520,7 +10614,7 @@ function JsonFieldEditor({ label, id, value, onChange, required }) {
10520
10614
  }
10521
10615
  function ImageFieldRenderer({ id, label, description, value, onChange, required }) {
10522
10616
  const { _: _t5 } = useLingui();
10523
- const [pickerOpen, setPickerOpen] = React.useState(false);
10617
+ const [pickerOpen, setPickerOpen] = React$1.useState(false);
10524
10618
  const displayUrl = typeof value === "string" ? value : value?.previewUrl || value?.src || (value && (!value.provider || value.provider === "local") ? `/_emdash/api/media/file/${typeof value.meta?.storageKey === "string" ? value.meta.storageKey : value.id}` : void 0);
10525
10619
  const handleSelect = (item) => {
10526
10620
  const isLocalProvider = !item.provider || item.provider === "local";
@@ -10612,19 +10706,165 @@ function ImageFieldRenderer({ id, label, description, value, onChange, required
10612
10706
  ]
10613
10707
  });
10614
10708
  }
10615
- function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuickEdit }) {
10709
+ /**
10710
+ * File field with media picker
10711
+ *
10712
+ * Like ImageFieldRenderer but for arbitrary file types. Shows a mime-type-appropriate
10713
+ * icon, filename, and size instead of an image preview.
10714
+ */
10715
+ function FileFieldRenderer({ id, label, value, onChange, required }) {
10616
10716
  const { _: _t6 } = useLingui();
10617
- const [selectedBylineId, setSelectedBylineId] = React.useState("");
10618
- const [quickName, setQuickName] = React.useState("");
10619
- const [quickSlug, setQuickSlug] = React.useState("");
10620
- const [quickError, setQuickError] = React.useState(null);
10621
- const [isCreating, setIsCreating] = React.useState(false);
10622
- const [editBylineId, setEditBylineId] = React.useState(null);
10623
- const [editName, setEditName] = React.useState("");
10624
- const [editSlug, setEditSlug] = React.useState("");
10625
- const [editError, setEditError] = React.useState(null);
10626
- const [isEditing, setIsEditing] = React.useState(false);
10627
- const bylineMap = React.useMemo(() => new Map(bylines.map((b) => [b.id, b])), [bylines]);
10717
+ const [pickerOpen, setPickerOpen] = React$1.useState(false);
10718
+ const normalized = React$1.useMemo(() => {
10719
+ if (!value) return null;
10720
+ const isLocal = !value.provider || value.provider === "local";
10721
+ const storageKey = typeof value.meta?.storageKey === "string" ? value.meta.storageKey : void 0;
10722
+ const localSrc = typeof value.src === "string" && value.src.startsWith("/_emdash/") ? value.src : void 0;
10723
+ const localUrl = isLocal ? storageKey ? `/_emdash/api/media/file/${encodeURIComponent(storageKey)}` : localSrc ?? `/_emdash/api/media/file/${encodeURIComponent(value.id)}` : void 0;
10724
+ const externalUrl = !isLocal && value.src && isSafeUrl$1(value.src) ? value.src : void 0;
10725
+ return {
10726
+ displayUrl: localUrl ?? externalUrl,
10727
+ filename: value.filename || _t6({
10728
+ id: "cChtk1",
10729
+ message: "Untitled file"
10730
+ }),
10731
+ mimeType: value.mimeType || "",
10732
+ size: value.size
10733
+ };
10734
+ }, [value, _t6]);
10735
+ const handleSelect = (item) => {
10736
+ const isLocalProvider = !item.provider || item.provider === "local";
10737
+ onChange({
10738
+ id: item.id,
10739
+ provider: item.provider || "local",
10740
+ src: isLocalProvider ? void 0 : item.url,
10741
+ filename: item.filename,
10742
+ mimeType: item.mimeType,
10743
+ size: item.size,
10744
+ meta: isLocalProvider ? {
10745
+ ...item.meta,
10746
+ storageKey: item.storageKey
10747
+ } : item.meta
10748
+ });
10749
+ };
10750
+ const handleRemove = () => {
10751
+ onChange(null);
10752
+ };
10753
+ const hasMime = !!normalized?.mimeType;
10754
+ const size = typeof normalized?.size === "number" ? normalized.size : void 0;
10755
+ const hasSize = size !== void 0;
10756
+ return /* @__PURE__ */ jsxs("div", {
10757
+ id,
10758
+ children: [
10759
+ /* @__PURE__ */ jsx(Label, { children: label }),
10760
+ normalized ? /* @__PURE__ */ jsxs("div", {
10761
+ className: "mt-2 flex items-center gap-3 rounded-lg border p-3",
10762
+ children: [
10763
+ /* @__PURE__ */ jsx("span", {
10764
+ className: "text-3xl",
10765
+ "aria-hidden": "true",
10766
+ children: getFileIcon(normalized.mimeType)
10767
+ }),
10768
+ /* @__PURE__ */ jsxs("div", {
10769
+ className: "flex-1 min-w-0",
10770
+ children: [normalized.displayUrl ? /* @__PURE__ */ jsx("a", {
10771
+ href: normalized.displayUrl,
10772
+ target: "_blank",
10773
+ rel: "noopener noreferrer",
10774
+ className: "text-sm font-medium truncate block hover:underline",
10775
+ children: normalized.filename
10776
+ }) : /* @__PURE__ */ jsx("p", {
10777
+ className: "text-sm font-medium truncate",
10778
+ children: normalized.filename
10779
+ }), (hasMime || hasSize) && /* @__PURE__ */ jsxs("p", {
10780
+ className: "text-xs text-kumo-subtle",
10781
+ children: [
10782
+ hasMime ? normalized.mimeType : null,
10783
+ hasMime && hasSize ? " • " : null,
10784
+ hasSize ? formatFileSize(size) : null
10785
+ ]
10786
+ })]
10787
+ }),
10788
+ /* @__PURE__ */ jsxs("div", {
10789
+ className: "flex gap-1",
10790
+ children: [/* @__PURE__ */ jsx(Button, {
10791
+ type: "button",
10792
+ size: "sm",
10793
+ variant: "secondary",
10794
+ onClick: () => setPickerOpen(true),
10795
+ children: _t6({
10796
+ id: "o+XJ9D",
10797
+ message: "Change"
10798
+ })
10799
+ }), /* @__PURE__ */ jsx(Button, {
10800
+ type: "button",
10801
+ shape: "square",
10802
+ variant: "destructive",
10803
+ className: "h-8 w-8",
10804
+ onClick: handleRemove,
10805
+ "aria-label": _t6({
10806
+ id: "HND1q3",
10807
+ message: "Remove {label}",
10808
+ values: { label }
10809
+ }),
10810
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
10811
+ })]
10812
+ })
10813
+ ]
10814
+ }) : /* @__PURE__ */ jsx(Button, {
10815
+ type: "button",
10816
+ variant: "outline",
10817
+ className: "mt-2 w-full h-32 border-dashed",
10818
+ onClick: () => setPickerOpen(true),
10819
+ "aria-label": _t6({
10820
+ id: "pov/PW",
10821
+ message: "Select {label}",
10822
+ values: { label }
10823
+ }),
10824
+ children: /* @__PURE__ */ jsxs("div", {
10825
+ className: "flex flex-col items-center gap-2 text-kumo-subtle",
10826
+ children: [/* @__PURE__ */ jsx(Paperclip, { className: "h-8 w-8" }), /* @__PURE__ */ jsx("span", { children: _t6({
10827
+ id: "J6WI1B",
10828
+ message: "Select file"
10829
+ }) })]
10830
+ })
10831
+ }),
10832
+ /* @__PURE__ */ jsx(MediaPickerModal, {
10833
+ open: pickerOpen,
10834
+ onOpenChange: setPickerOpen,
10835
+ onSelect: handleSelect,
10836
+ mimeTypeFilter: "",
10837
+ hideUrlInput: true,
10838
+ mediaKind: "file",
10839
+ title: _t6({
10840
+ id: "pov/PW",
10841
+ message: "Select {label}",
10842
+ values: { label }
10843
+ })
10844
+ }),
10845
+ required && !normalized && /* @__PURE__ */ jsx("p", {
10846
+ className: "text-sm text-kumo-danger mt-1",
10847
+ children: _t6({
10848
+ id: "B8AaMI",
10849
+ message: "This field is required"
10850
+ })
10851
+ })
10852
+ ]
10853
+ });
10854
+ }
10855
+ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuickEdit }) {
10856
+ const { _: _t7 } = useLingui();
10857
+ const [selectedBylineId, setSelectedBylineId] = React$1.useState("");
10858
+ const [quickName, setQuickName] = React$1.useState("");
10859
+ const [quickSlug, setQuickSlug] = React$1.useState("");
10860
+ const [quickError, setQuickError] = React$1.useState(null);
10861
+ const [isCreating, setIsCreating] = React$1.useState(false);
10862
+ const [editBylineId, setEditBylineId] = React$1.useState(null);
10863
+ const [editName, setEditName] = React$1.useState("");
10864
+ const [editSlug, setEditSlug] = React$1.useState("");
10865
+ const [editError, setEditError] = React$1.useState(null);
10866
+ const [isEditing, setIsEditing] = React$1.useState(false);
10867
+ const bylineMap = React$1.useMemo(() => new Map(bylines.map((b) => [b.id, b])), [bylines]);
10628
10868
  const availableToAdd = bylines.filter((b) => !credits.some((c) => c.bylineId === b.id));
10629
10869
  const move = (index, direction) => {
10630
10870
  const target = index + direction;
@@ -10663,7 +10903,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10663
10903
  className: "w-full rounded border bg-kumo-base px-3 py-2 text-sm",
10664
10904
  children: [/* @__PURE__ */ jsx("option", {
10665
10905
  value: "",
10666
- children: _t6({
10906
+ children: _t7({
10667
10907
  id: "r5zcIE",
10668
10908
  message: "Select byline..."
10669
10909
  })
@@ -10683,7 +10923,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10683
10923
  setSelectedBylineId("");
10684
10924
  },
10685
10925
  disabled: !selectedBylineId,
10686
- children: _t6({
10926
+ children: _t7({
10687
10927
  id: "m16xKo",
10688
10928
  message: "Add"
10689
10929
  })
@@ -10712,7 +10952,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10712
10952
  variant: "ghost",
10713
10953
  size: "sm",
10714
10954
  onClick: () => move(index, -1),
10715
- children: _t6({
10955
+ children: _t7({
10716
10956
  id: "n+PHuv",
10717
10957
  message: "Up"
10718
10958
  })
@@ -10722,7 +10962,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10722
10962
  variant: "ghost",
10723
10963
  size: "sm",
10724
10964
  onClick: () => move(index, 1),
10725
- children: _t6({
10965
+ children: _t7({
10726
10966
  id: "gUjkl/",
10727
10967
  message: "Down"
10728
10968
  })
@@ -10732,7 +10972,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10732
10972
  variant: "ghost",
10733
10973
  size: "sm",
10734
10974
  onClick: () => openEditByline(byline),
10735
- children: _t6({
10975
+ children: _t7({
10736
10976
  id: "ePK91l",
10737
10977
  message: "Edit"
10738
10978
  })
@@ -10742,7 +10982,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10742
10982
  variant: "destructive",
10743
10983
  size: "sm",
10744
10984
  onClick: () => onChange(credits.filter((_, i) => i !== index)),
10745
- children: _t6({
10985
+ children: _t7({
10746
10986
  id: "t/YqKh",
10747
10987
  message: "Remove"
10748
10988
  })
@@ -10750,7 +10990,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10750
10990
  ]
10751
10991
  })]
10752
10992
  }), /* @__PURE__ */ jsx(Input, {
10753
- label: _t6({
10993
+ label: _t7({
10754
10994
  id: "QeScoL",
10755
10995
  message: "Role label"
10756
10996
  }),
@@ -10770,7 +11010,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10770
11010
  })
10771
11011
  }) : /* @__PURE__ */ jsx("p", {
10772
11012
  className: "text-sm text-kumo-subtle",
10773
- children: _t6({
11013
+ children: _t7({
10774
11014
  id: "dyZY24",
10775
11015
  message: "No bylines selected."
10776
11016
  })
@@ -10779,7 +11019,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10779
11019
  ...p,
10780
11020
  type: "button",
10781
11021
  variant: "secondary",
10782
- children: _t6({
11022
+ children: _t7({
10783
11023
  id: "gqa8eq",
10784
11024
  message: "Quick create byline"
10785
11025
  })
@@ -10789,7 +11029,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10789
11029
  children: [
10790
11030
  /* @__PURE__ */ jsx(Dialog.Title, {
10791
11031
  className: "text-lg font-semibold",
10792
- children: _t6({
11032
+ children: _t7({
10793
11033
  id: "d6HWlw",
10794
11034
  message: "Create byline"
10795
11035
  })
@@ -10798,7 +11038,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10798
11038
  className: "mt-4 space-y-3",
10799
11039
  children: [
10800
11040
  /* @__PURE__ */ jsx(Input, {
10801
- label: _t6({
11041
+ label: _t7({
10802
11042
  id: "pfa8F0",
10803
11043
  message: "Display name"
10804
11044
  }),
@@ -10809,7 +11049,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10809
11049
  }
10810
11050
  }),
10811
11051
  /* @__PURE__ */ jsx(Input, {
10812
- label: _t6({
11052
+ label: _t7({
10813
11053
  id: "L85WcV",
10814
11054
  message: "Slug"
10815
11055
  }),
@@ -10831,7 +11071,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10831
11071
  resetQuickCreate();
10832
11072
  p.onClick?.(e);
10833
11073
  },
10834
- children: _t6({
11074
+ children: _t7({
10835
11075
  id: "dEgA5A",
10836
11076
  message: "Cancel"
10837
11077
  })
@@ -10852,7 +11092,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10852
11092
  }]);
10853
11093
  resetQuickCreate();
10854
11094
  } catch (err) {
10855
- setQuickError(err instanceof Error ? err.message : _t6({
11095
+ setQuickError(err instanceof Error ? err.message : _t7({
10856
11096
  id: "Kd9Ewv",
10857
11097
  message: "Failed to create byline"
10858
11098
  }));
@@ -10860,10 +11100,10 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10860
11100
  setIsCreating(false);
10861
11101
  }
10862
11102
  },
10863
- children: isCreating ? _t6({
11103
+ children: isCreating ? _t7({
10864
11104
  id: "HM56Bx",
10865
11105
  message: "Creating..."
10866
- }) : _t6({
11106
+ }) : _t7({
10867
11107
  id: "hYgDIe",
10868
11108
  message: "Create"
10869
11109
  })
@@ -10880,7 +11120,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10880
11120
  children: [
10881
11121
  /* @__PURE__ */ jsx(Dialog.Title, {
10882
11122
  className: "text-lg font-semibold",
10883
- children: _t6({
11123
+ children: _t7({
10884
11124
  id: "d9lVyw",
10885
11125
  message: "Edit byline"
10886
11126
  })
@@ -10889,7 +11129,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10889
11129
  className: "mt-4 space-y-3",
10890
11130
  children: [
10891
11131
  /* @__PURE__ */ jsx(Input, {
10892
- label: _t6({
11132
+ label: _t7({
10893
11133
  id: "pfa8F0",
10894
11134
  message: "Display name"
10895
11135
  }),
@@ -10900,7 +11140,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10900
11140
  }
10901
11141
  }),
10902
11142
  /* @__PURE__ */ jsx(Input, {
10903
- label: _t6({
11143
+ label: _t7({
10904
11144
  id: "L85WcV",
10905
11145
  message: "Slug"
10906
11146
  }),
@@ -10919,7 +11159,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10919
11159
  type: "button",
10920
11160
  variant: "secondary",
10921
11161
  onClick: resetQuickEdit,
10922
- children: _t6({
11162
+ children: _t7({
10923
11163
  id: "dEgA5A",
10924
11164
  message: "Cancel"
10925
11165
  })
@@ -10936,7 +11176,7 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10936
11176
  });
10937
11177
  resetQuickEdit();
10938
11178
  } catch (err) {
10939
- setEditError(err instanceof Error ? err.message : _t6({
11179
+ setEditError(err instanceof Error ? err.message : _t7({
10940
11180
  id: "fd6o+d",
10941
11181
  message: "Failed to update byline"
10942
11182
  }));
@@ -10944,10 +11184,10 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10944
11184
  setIsEditing(false);
10945
11185
  }
10946
11186
  },
10947
- children: isEditing ? _t6({
11187
+ children: isEditing ? _t7({
10948
11188
  id: "XvjC4F",
10949
11189
  message: "Saving..."
10950
- }) : _t6({
11190
+ }) : _t7({
10951
11191
  id: "tfDRzk",
10952
11192
  message: "Save"
10953
11193
  })
@@ -10960,9 +11200,9 @@ function BylineCreditsEditor({ credits, bylines, onChange, onQuickCreate, onQuic
10960
11200
  });
10961
11201
  }
10962
11202
  function AuthorSelector({ authorId, users, onChange }) {
10963
- const { _: _t7 } = useLingui();
11203
+ const { _: _t8 } = useLingui();
10964
11204
  const currentAuthor = users.find((u) => u.id === authorId);
10965
- const authorItems = { unassigned: _t7({
11205
+ const authorItems = { unassigned: _t8({
10966
11206
  id: "EbMPZJ",
10967
11207
  message: "Unassigned"
10968
11208
  }) };
@@ -10977,7 +11217,7 @@ function AuthorSelector({ authorId, users, onChange }) {
10977
11217
  value: "unassigned",
10978
11218
  children: /* @__PURE__ */ jsx("span", {
10979
11219
  className: "text-kumo-subtle",
10980
- children: _t7({
11220
+ children: _t8({
10981
11221
  id: "EbMPZJ",
10982
11222
  message: "Unassigned"
10983
11223
  })
@@ -11044,6 +11284,16 @@ function LocaleSwitcher({ locales, defaultLocale, value, onChange, showAll = fal
11044
11284
  })]
11045
11285
  });
11046
11286
  }
11287
+ /**
11288
+ * Hook to get i18n config from the manifest query.
11289
+ * Returns null if i18n is not configured.
11290
+ */
11291
+ function useI18nConfig(manifest) {
11292
+ return React.useMemo(() => {
11293
+ if (!manifest?.i18n) return null;
11294
+ return manifest.i18n;
11295
+ }, [manifest?.i18n]);
11296
+ }
11047
11297
 
11048
11298
  //#endregion
11049
11299
  //#region src/components/ContentList.tsx
@@ -11058,21 +11308,21 @@ function getItemTitle$1(item) {
11058
11308
  */
11059
11309
  function ContentList({ collection, collectionLabel, items, trashedItems = [], isLoading, isTrashedLoading, onDelete, onDuplicate, onRestore, onPermanentDelete, onLoadMore, onLoadMoreTrashed, hasMore, hasMoreTrashed, trashedCount = 0, i18n: i18n$1, activeLocale, onLocaleChange, urlPattern, sort, onSortChange }) {
11060
11310
  const { _: _t } = useLingui();
11061
- const [activeTab, setActiveTab] = React.useState("all");
11062
- const [searchQuery, setSearchQuery] = React.useState("");
11063
- const [page, setPage] = React.useState(0);
11311
+ const [activeTab, setActiveTab] = React$1.useState("all");
11312
+ const [searchQuery, setSearchQuery] = React$1.useState("");
11313
+ const [page, setPage] = React$1.useState(0);
11064
11314
  const handleSearchChange = (e) => {
11065
11315
  setSearchQuery(e.target.value);
11066
11316
  setPage(0);
11067
11317
  };
11068
- const filteredItems = React.useMemo(() => {
11318
+ const filteredItems = React$1.useMemo(() => {
11069
11319
  if (!searchQuery) return items;
11070
11320
  const query = searchQuery.toLowerCase();
11071
11321
  return items.filter((item) => getItemTitle$1(item).toLowerCase().includes(query));
11072
11322
  }, [items, searchQuery]);
11073
11323
  const totalPages = Math.max(1, Math.ceil(filteredItems.length / PAGE_SIZE));
11074
11324
  const paginatedItems = filteredItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);
11075
- React.useEffect(() => {
11325
+ React$1.useEffect(() => {
11076
11326
  if (page >= totalPages - 1 && hasMore && onLoadMore && !searchQuery) onLoadMore();
11077
11327
  }, [
11078
11328
  page,
@@ -11822,8 +12072,8 @@ function getInitialFormState(field) {
11822
12072
  */
11823
12073
  function FieldEditor({ open, onOpenChange, field, onSave, isSaving }) {
11824
12074
  const { _: _t } = useLingui();
11825
- const [formState, setFormState] = React.useState(() => getInitialFormState(field));
11826
- React.useEffect(() => {
12075
+ const [formState, setFormState] = React$1.useState(() => getInitialFormState(field));
12076
+ React$1.useEffect(() => {
11827
12077
  if (open) setFormState(getInitialFormState(field));
11828
12078
  }, [open, field]);
11829
12079
  const { step, selectedType, slug, label, required, unique, searchable } = formState;
@@ -12709,23 +12959,23 @@ const SYSTEM_FIELDS = [
12709
12959
  function ContentTypeEditor({ collection, isNew, isSaving, onSave, onAddField, onUpdateField, onDeleteField, onReorderFields }) {
12710
12960
  const { _: _t } = useLingui();
12711
12961
  useNavigate$1();
12712
- const [slug, setSlug] = React.useState(collection?.slug ?? "");
12713
- const [label, setLabel] = React.useState(collection?.label ?? "");
12714
- const [labelSingular, setLabelSingular] = React.useState(collection?.labelSingular ?? "");
12715
- const [description, setDescription] = React.useState(collection?.description ?? "");
12716
- const [urlPattern, setUrlPattern] = React.useState(collection?.urlPattern ?? "");
12717
- const [supports, setSupports] = React.useState((collection?.supports ?? ["drafts", "revisions"]).filter((s) => s !== "seo"));
12718
- const [hasSeo, setHasSeo] = React.useState(collection?.hasSeo ?? false);
12719
- const [commentsEnabled, setCommentsEnabled] = React.useState(collection?.commentsEnabled ?? false);
12720
- const [commentsModeration, setCommentsModeration] = React.useState(collection?.commentsModeration ?? "first_time");
12721
- const [commentsClosedAfterDays, setCommentsClosedAfterDays] = React.useState(collection?.commentsClosedAfterDays ?? 90);
12722
- const [commentsAutoApproveUsers, setCommentsAutoApproveUsers] = React.useState(collection?.commentsAutoApproveUsers ?? true);
12723
- const [fieldEditorOpen, setFieldEditorOpen] = React.useState(false);
12724
- const [editingField, setEditingField] = React.useState();
12725
- const [fieldSaving, setFieldSaving] = React.useState(false);
12726
- const [deleteFieldTarget, setDeleteFieldTarget] = React.useState(null);
12962
+ const [slug, setSlug] = React$1.useState(collection?.slug ?? "");
12963
+ const [label, setLabel] = React$1.useState(collection?.label ?? "");
12964
+ const [labelSingular, setLabelSingular] = React$1.useState(collection?.labelSingular ?? "");
12965
+ const [description, setDescription] = React$1.useState(collection?.description ?? "");
12966
+ const [urlPattern, setUrlPattern] = React$1.useState(collection?.urlPattern ?? "");
12967
+ const [supports, setSupports] = React$1.useState((collection?.supports ?? ["drafts", "revisions"]).filter((s) => s !== "seo"));
12968
+ const [hasSeo, setHasSeo] = React$1.useState(collection?.hasSeo ?? false);
12969
+ const [commentsEnabled, setCommentsEnabled] = React$1.useState(collection?.commentsEnabled ?? false);
12970
+ const [commentsModeration, setCommentsModeration] = React$1.useState(collection?.commentsModeration ?? "first_time");
12971
+ const [commentsClosedAfterDays, setCommentsClosedAfterDays] = React$1.useState(collection?.commentsClosedAfterDays ?? 90);
12972
+ const [commentsAutoApproveUsers, setCommentsAutoApproveUsers] = React$1.useState(collection?.commentsAutoApproveUsers ?? true);
12973
+ const [fieldEditorOpen, setFieldEditorOpen] = React$1.useState(false);
12974
+ const [editingField, setEditingField] = React$1.useState();
12975
+ const [fieldSaving, setFieldSaving] = React$1.useState(false);
12976
+ const [deleteFieldTarget, setDeleteFieldTarget] = React$1.useState(null);
12727
12977
  const urlPatternValid = !urlPattern || urlPattern.includes("{slug}");
12728
- const hasChanges = React.useMemo(() => {
12978
+ const hasChanges = React$1.useMemo(() => {
12729
12979
  if (isNew) return slug && label;
12730
12980
  if (!collection) return false;
12731
12981
  return label !== collection.label || labelSingular !== (collection.labelSingular ?? "") || description !== (collection.description ?? "") || urlPattern !== (collection.urlPattern ?? "") || JSON.stringify([...supports].toSorted()) !== JSON.stringify(collection.supports.filter((s) => s !== "seo").toSorted()) || hasSeo !== collection.hasSeo || commentsEnabled !== collection.commentsEnabled || commentsModeration !== collection.commentsModeration || commentsClosedAfterDays !== collection.commentsClosedAfterDays || commentsAutoApproveUsers !== collection.commentsAutoApproveUsers;
@@ -13267,7 +13517,7 @@ function SystemFieldRow({ field }) {
13267
13517
  */
13268
13518
  function ContentTypeList({ collections, orphanedTables, isLoading, onDelete, onRegisterOrphan }) {
13269
13519
  const { _: _t } = useLingui();
13270
- const [deleteTarget, setDeleteTarget] = React.useState(null);
13520
+ const [deleteTarget, setDeleteTarget] = React$1.useState(null);
13271
13521
  const hasOrphans = orphanedTables && orphanedTables.length > 0;
13272
13522
  return /* @__PURE__ */ jsxs("div", {
13273
13523
  className: "space-y-4",
@@ -13942,9 +14192,9 @@ const ROLE_NAMES = {
13942
14192
  const DEVICE_CODE_INVALID_CHARS_REGEX = /[^A-Z0-9-]/g;
13943
14193
  const DEVICE_CODE_HYPHEN_REGEX = /-/g;
13944
14194
  function DeviceAuthorizePage() {
13945
- const [code, setCode] = React.useState("");
13946
- const [pageState, setPageState] = React.useState("input");
13947
- const [errorMessage, setErrorMessage] = React.useState("");
14195
+ const [code, setCode] = React$1.useState("");
14196
+ const [pageState, setPageState] = React$1.useState("input");
14197
+ const [errorMessage, setErrorMessage] = React$1.useState("");
13948
14198
  const { data: user, isLoading, error: authError } = useQuery({
13949
14199
  queryKey: ["auth-me"],
13950
14200
  queryFn: async () => {
@@ -13952,11 +14202,11 @@ function DeviceAuthorizePage() {
13952
14202
  },
13953
14203
  retry: false
13954
14204
  });
13955
- React.useEffect(() => {
14205
+ React$1.useEffect(() => {
13956
14206
  const urlCode = new URLSearchParams(window.location.search).get("code");
13957
14207
  if (urlCode) setCode(urlCode);
13958
14208
  }, []);
13959
- React.useEffect(() => {
14209
+ React$1.useEffect(() => {
13960
14210
  if (!isLoading && (authError || !user)) {
13961
14211
  const returnUrl = encodeURIComponent(window.location.pathname + window.location.search);
13962
14212
  window.location.href = `/_emdash/admin/login?redirect=${returnUrl}`;
@@ -14313,11 +14563,11 @@ function PasskeyRegistration({ optionsEndpoint, verifyEndpoint, onSuccess, onErr
14313
14563
  id: "0QF2SD",
14314
14564
  message: "Register Passkey"
14315
14565
  });
14316
- const [state, setState] = React.useState({ status: "idle" });
14317
- const [passkeyName, setPasskeyName] = React.useState("");
14318
- const isSupported = React.useMemo(() => isPasskeyEnvironmentUsable(), []);
14319
- const insecureContext = React.useMemo(() => typeof window !== "undefined" && !isWebAuthnSecureContext(), []);
14320
- const handleRegister = React.useCallback(async () => {
14566
+ const [state, setState] = React$1.useState({ status: "idle" });
14567
+ const [passkeyName, setPasskeyName] = React$1.useState("");
14568
+ const isSupported = React$1.useMemo(() => isPasskeyEnvironmentUsable(), []);
14569
+ const insecureContext = React$1.useMemo(() => typeof window !== "undefined" && !isWebAuthnSecureContext(), []);
14570
+ const handleRegister = React$1.useCallback(async () => {
14321
14571
  if (!isSupported) {
14322
14572
  setState({
14323
14573
  status: "error",
@@ -14849,7 +15099,7 @@ function handleInviteSuccess() {
14849
15099
  window.location.href = "/_emdash/admin";
14850
15100
  }
14851
15101
  function RegisterStep({ inviteData, token }) {
14852
- const [name, setName] = React.useState("");
15102
+ const [name, setName] = React$1.useState("");
14853
15103
  return /* @__PURE__ */ jsxs("div", {
14854
15104
  className: "space-y-6",
14855
15105
  children: [
@@ -14980,13 +15230,13 @@ function ErrorStep$1({ message, code }) {
14980
15230
  }
14981
15231
  function InviteAcceptPage() {
14982
15232
  const { token: urlToken } = useSearch({ strict: false });
14983
- const [step, setStep] = React.useState("verify");
14984
- const [error, setError] = React.useState();
14985
- const [errorCode, setErrorCode] = React.useState();
14986
- const [isLoading, setIsLoading] = React.useState(true);
14987
- const [inviteData, setInviteData] = React.useState(null);
14988
- const [token, setToken] = React.useState(null);
14989
- React.useEffect(() => {
15233
+ const [step, setStep] = React$1.useState("verify");
15234
+ const [error, setError] = React$1.useState();
15235
+ const [errorCode, setErrorCode] = React$1.useState();
15236
+ const [isLoading, setIsLoading] = React$1.useState(true);
15237
+ const [inviteData, setInviteData] = React$1.useState(null);
15238
+ const [token, setToken] = React$1.useState(null);
15239
+ React$1.useEffect(() => {
14990
15240
  if (!urlToken) {
14991
15241
  setError("No invite token provided");
14992
15242
  setStep("error");
@@ -15105,15 +15355,15 @@ function PasskeyLogin({ optionsEndpoint, verifyEndpoint, onSuccess, onError, sho
15105
15355
  id: "QjsOMP",
15106
15356
  message: "Sign in with Passkey"
15107
15357
  });
15108
- const [state, setState] = React.useState({ status: "idle" });
15109
- const [email, setEmail] = React.useState("");
15110
- const [supportsConditional, setSupportsConditional] = React.useState(false);
15111
- const isSupported = React.useMemo(() => isPasskeyEnvironmentUsable(), []);
15112
- const insecureContext = React.useMemo(() => typeof window !== "undefined" && !isWebAuthnSecureContext(), []);
15113
- React.useEffect(() => {
15358
+ const [state, setState] = React$1.useState({ status: "idle" });
15359
+ const [email, setEmail] = React$1.useState("");
15360
+ const [supportsConditional, setSupportsConditional] = React$1.useState(false);
15361
+ const isSupported = React$1.useMemo(() => isPasskeyEnvironmentUsable(), []);
15362
+ const insecureContext = React$1.useMemo(() => typeof window !== "undefined" && !isWebAuthnSecureContext(), []);
15363
+ React$1.useEffect(() => {
15114
15364
  isConditionalMediationSupported().then(setSupportsConditional);
15115
15365
  }, []);
15116
- const handleLogin = React.useCallback(async (useConditional = false) => {
15366
+ const handleLogin = React$1.useCallback(async (useConditional = false) => {
15117
15367
  if (!isSupported) {
15118
15368
  setState({
15119
15369
  status: "error",
@@ -15385,10 +15635,10 @@ function PasskeyLogin({ optionsEndpoint, verifyEndpoint, onSuccess, onError, sho
15385
15635
  */
15386
15636
  function MagicLinkForm({ onBack }) {
15387
15637
  const { _: _t } = useLingui();
15388
- const [email, setEmail] = React.useState("");
15389
- const [isLoading, setIsLoading] = React.useState(false);
15390
- const [error, setError] = React.useState(null);
15391
- const [sent, setSent] = React.useState(false);
15638
+ const [email, setEmail] = React$1.useState("");
15639
+ const [isLoading, setIsLoading] = React$1.useState(false);
15640
+ const [error, setError] = React$1.useState(null);
15641
+ const [sent, setSent] = React$1.useState(false);
15392
15642
  const handleSubmit = async (e) => {
15393
15643
  e.preventDefault();
15394
15644
  setError(null);
@@ -15527,18 +15777,18 @@ function LoginPage({ redirectUrl = "/_emdash/admin" }) {
15527
15777
  const safeRedirectUrl = sanitizeRedirectUrl(redirectUrl);
15528
15778
  const { _: _t2 } = useLingui();
15529
15779
  const { locale, setLocale } = useLocale();
15530
- const [method, setMethod] = React.useState("passkey");
15531
- const [urlError, setUrlError] = React.useState(null);
15532
- const [activeProvider, setActiveProvider] = React.useState(null);
15780
+ const [method, setMethod] = React$1.useState("passkey");
15781
+ const [urlError, setUrlError] = React$1.useState(null);
15782
+ const [activeProvider, setActiveProvider] = React$1.useState(null);
15533
15783
  const authProviderList = useAuthProviderList();
15534
15784
  const { data: authInfo, isLoading: authModeLoading } = useQuery({
15535
15785
  queryKey: ["authMode"],
15536
15786
  queryFn: fetchAuthMode
15537
15787
  });
15538
- React.useEffect(() => {
15788
+ React$1.useEffect(() => {
15539
15789
  if (authInfo?.authMode && authInfo.authMode !== "passkey") window.location.href = safeRedirectUrl;
15540
15790
  }, [authInfo, safeRedirectUrl]);
15541
- React.useEffect(() => {
15791
+ React$1.useEffect(() => {
15542
15792
  const params = new URLSearchParams(window.location.search);
15543
15793
  const error = params.get("error");
15544
15794
  const message = params.get("message");
@@ -15731,11 +15981,11 @@ const SORT_LABELS$1 = {
15731
15981
  };
15732
15982
  function MarketplaceBrowse({ installedPluginIds = /* @__PURE__ */ new Set() }) {
15733
15983
  const { _: _t } = useLingui();
15734
- const [searchQuery, setSearchQuery] = React.useState("");
15735
- const [sort, setSort] = React.useState("installs");
15736
- const [capability, setCapability] = React.useState("");
15737
- const [debouncedQuery, setDebouncedQuery] = React.useState("");
15738
- React.useEffect(() => {
15984
+ const [searchQuery, setSearchQuery] = React$1.useState("");
15985
+ const [sort, setSort] = React$1.useState("installs");
15986
+ const [capability, setCapability] = React$1.useState("");
15987
+ const [debouncedQuery, setDebouncedQuery] = React$1.useState("");
15988
+ React$1.useEffect(() => {
15739
15989
  const timer = setTimeout(setDebouncedQuery, 300, searchQuery);
15740
15990
  return () => clearTimeout(timer);
15741
15991
  }, [searchQuery]);
@@ -16248,7 +16498,7 @@ function PluginManager({ manifest }) {
16248
16498
  });
16249
16499
  }
16250
16500
  });
16251
- const updateMap = React.useMemo(() => {
16501
+ const updateMap = React$1.useMemo(() => {
16252
16502
  if (!updates) return /* @__PURE__ */ new Map();
16253
16503
  return new Map(updates.map((u) => [u.pluginId, u]));
16254
16504
  }, [updates]);
@@ -16392,9 +16642,9 @@ function PluginManager({ manifest }) {
16392
16642
  }
16393
16643
  function PluginCard({ plugin, updateInfo, onEnable, onDisable, isToggling, hasMarketplace }) {
16394
16644
  const { _: _t2 } = useLingui();
16395
- const [expanded, setExpanded] = React.useState(false);
16396
- const [showUpdateConsent, setShowUpdateConsent] = React.useState(false);
16397
- const [showUninstallConfirm, setShowUninstallConfirm] = React.useState(false);
16645
+ const [expanded, setExpanded] = React$1.useState(false);
16646
+ const [showUpdateConsent, setShowUpdateConsent] = React$1.useState(false);
16647
+ const [showUninstallConfirm, setShowUninstallConfirm] = React$1.useState(false);
16398
16648
  const queryClient = useQueryClient();
16399
16649
  const toastManager = Toast.useToastManager();
16400
16650
  const isMarketplace = plugin.source === "marketplace";
@@ -16761,7 +17011,7 @@ function PluginCard({ plugin, updateInfo, onEnable, onDisable, isToggling, hasMa
16761
17011
  }
16762
17012
  function UninstallConfirmDialog({ pluginName, isPending, error, onConfirm, onCancel }) {
16763
17013
  const { _: _t3 } = useLingui();
16764
- const [deleteData, setDeleteData] = React.useState(false);
17014
+ const [deleteData, setDeleteData] = React$1.useState(false);
16765
17015
  return /* @__PURE__ */ jsxs("div", {
16766
17016
  className: "fixed inset-0 z-50 flex items-center justify-center",
16767
17017
  role: "dialog",
@@ -16850,9 +17100,9 @@ function UninstallConfirmDialog({ pluginName, isPending, error, onConfirm, onCan
16850
17100
  function MarketplacePluginDetail({ pluginId, installedPluginIds = /* @__PURE__ */ new Set() }) {
16851
17101
  const { _: _t } = useLingui();
16852
17102
  const queryClient = useQueryClient();
16853
- const [showConsent, setShowConsent] = React.useState(false);
16854
- const [showUninstallConfirm, setShowUninstallConfirm] = React.useState(false);
16855
- const [lightboxIndex, setLightboxIndex] = React.useState(null);
17103
+ const [showConsent, setShowConsent] = React$1.useState(false);
17104
+ const [showUninstallConfirm, setShowUninstallConfirm] = React$1.useState(false);
17105
+ const [lightboxIndex, setLightboxIndex] = React$1.useState(null);
16856
17106
  const { data: plugin, isLoading, error } = useQuery({
16857
17107
  queryKey: [
16858
17108
  "marketplace",
@@ -17229,7 +17479,7 @@ function BackLink() {
17229
17479
  }
17230
17480
  function ScreenshotLightbox({ screenshots, index, isBlurred = false, onClose, onNavigate }) {
17231
17481
  const { _: _t3 } = useLingui();
17232
- const handleKeyDown = React.useCallback((e) => {
17482
+ const handleKeyDown = React$1.useCallback((e) => {
17233
17483
  if (e.key === "Escape") onClose();
17234
17484
  if (e.key === "ArrowLeft" && index > 0) onNavigate(index - 1);
17235
17485
  if (e.key === "ArrowRight" && index < screenshots.length - 1) onNavigate(index + 1);
@@ -17239,7 +17489,7 @@ function ScreenshotLightbox({ screenshots, index, isBlurred = false, onClose, on
17239
17489
  onClose,
17240
17490
  onNavigate
17241
17491
  ]);
17242
- React.useEffect(() => {
17492
+ React$1.useEffect(() => {
17243
17493
  document.addEventListener("keydown", handleKeyDown);
17244
17494
  return () => document.removeEventListener("keydown", handleKeyDown);
17245
17495
  }, [handleKeyDown]);
@@ -17388,17 +17638,17 @@ function formatBytes(bytes) {
17388
17638
  function MediaDetailPanel({ item, onClose, onDeleted }) {
17389
17639
  const { _: _t } = useLingui();
17390
17640
  const queryClient = useQueryClient();
17391
- const [filename, setFilename] = React.useState(item?.filename ?? "");
17392
- const [alt, setAlt] = React.useState(item?.alt ?? "");
17393
- const [caption, setCaption] = React.useState(item?.caption ?? "");
17394
- React.useEffect(() => {
17641
+ const [filename, setFilename] = React$1.useState(item?.filename ?? "");
17642
+ const [alt, setAlt] = React$1.useState(item?.alt ?? "");
17643
+ const [caption, setCaption] = React$1.useState(item?.caption ?? "");
17644
+ React$1.useEffect(() => {
17395
17645
  if (item) {
17396
17646
  setFilename(item.filename);
17397
17647
  setAlt(item.alt ?? "");
17398
17648
  setCaption(item.caption ?? "");
17399
17649
  }
17400
17650
  }, [item]);
17401
- const hasChanges = React.useMemo(() => {
17651
+ const hasChanges = React$1.useMemo(() => {
17402
17652
  if (!item) return false;
17403
17653
  return filename !== item.filename || alt !== (item.alt ?? "") || caption !== (item.caption ?? "");
17404
17654
  }, [
@@ -17434,14 +17684,14 @@ function MediaDetailPanel({ item, onClose, onDeleted }) {
17434
17684
  caption: caption || void 0
17435
17685
  });
17436
17686
  };
17437
- const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
17687
+ const [showDeleteConfirm, setShowDeleteConfirm] = React$1.useState(false);
17438
17688
  const handleDelete = () => {
17439
17689
  if (!item) return;
17440
17690
  setShowDeleteConfirm(true);
17441
17691
  };
17442
17692
  const stableOnClose = useStableCallback(onClose);
17443
17693
  const stableHandleSave = useStableCallback(handleSave);
17444
- React.useEffect(() => {
17694
+ React$1.useEffect(() => {
17445
17695
  const handleKeyDown = (e) => {
17446
17696
  if (e.key === "Escape") stableOnClose();
17447
17697
  if ((e.metaKey || e.ctrlKey) && e.key === "s") {
@@ -17698,13 +17948,13 @@ function formatDate(isoString) {
17698
17948
  */
17699
17949
  function MediaLibrary({ items = [], isLoading, onUpload, onDelete, onItemUpdated }) {
17700
17950
  const { _: _t } = useLingui();
17701
- const [viewMode, setViewMode] = React.useState("grid");
17702
- const [selectedItem, setSelectedItem] = React.useState(null);
17703
- const [activeProvider, setActiveProvider] = React.useState("local");
17704
- const [searchQuery, setSearchQuery] = React.useState("");
17705
- const [uploadState, setUploadState] = React.useState({ status: "idle" });
17706
- const fileInputRef = React.useRef(null);
17707
- const [loadedDimensions, setLoadedDimensions] = React.useState({});
17951
+ const [viewMode, setViewMode] = React$1.useState("grid");
17952
+ const [selectedItem, setSelectedItem] = React$1.useState(null);
17953
+ const [activeProvider, setActiveProvider] = React$1.useState("local");
17954
+ const [searchQuery, setSearchQuery] = React$1.useState("");
17955
+ const [uploadState, setUploadState] = React$1.useState({ status: "idle" });
17956
+ const fileInputRef = React$1.useRef(null);
17957
+ const [loadedDimensions, setLoadedDimensions] = React$1.useState({});
17708
17958
  const { data: providers } = useQuery({
17709
17959
  queryKey: ["media-providers"],
17710
17960
  queryFn: fetchMediaProviders,
@@ -17722,7 +17972,7 @@ function MediaLibrary({ items = [], isLoading, onUpload, onDelete, onItemUpdated
17722
17972
  }),
17723
17973
  enabled: activeProvider !== "local"
17724
17974
  });
17725
- const activeProviderInfo = React.useMemo(() => {
17975
+ const activeProviderInfo = React$1.useMemo(() => {
17726
17976
  if (activeProvider === "local") return {
17727
17977
  id: "local",
17728
17978
  name: "Library",
@@ -17735,7 +17985,7 @@ function MediaLibrary({ items = [], isLoading, onUpload, onDelete, onItemUpdated
17735
17985
  };
17736
17986
  return providers?.find((p) => p.id === activeProvider);
17737
17987
  }, [activeProvider, providers]);
17738
- React.useEffect(() => {
17988
+ React$1.useEffect(() => {
17739
17989
  if (selectedItem && activeProvider === "local") {
17740
17990
  const updated = items.find((i) => i.id === selectedItem.id);
17741
17991
  if (updated) setSelectedItem(updated);
@@ -17746,7 +17996,7 @@ function MediaLibrary({ items = [], isLoading, onUpload, onDelete, onItemUpdated
17746
17996
  selectedItem?.id,
17747
17997
  activeProvider
17748
17998
  ]);
17749
- React.useEffect(() => {
17999
+ React$1.useEffect(() => {
17750
18000
  if (uploadState.status === "success" || uploadState.status === "error") {
17751
18001
  const timer = setTimeout(() => {
17752
18002
  setUploadState({ status: "idle" });
@@ -17870,7 +18120,7 @@ function MediaLibrary({ items = [], isLoading, onUpload, onDelete, onItemUpdated
17870
18120
  }
17871
18121
  if (fileInputRef.current) fileInputRef.current.value = "";
17872
18122
  };
17873
- const providerTabs = React.useMemo(() => {
18123
+ const providerTabs = React$1.useMemo(() => {
17874
18124
  const tabs = [{
17875
18125
  id: "local",
17876
18126
  name: "Library",
@@ -18384,18 +18634,18 @@ function getItemTitle(item) {
18384
18634
  }
18385
18635
  function ContentPickerModal({ open, onOpenChange, onSelect }) {
18386
18636
  const { _: _t } = useLingui();
18387
- const [searchQuery, setSearchQuery] = React.useState("");
18637
+ const [searchQuery, setSearchQuery] = React$1.useState("");
18388
18638
  const debouncedSearch = useDebouncedValue$1(searchQuery, 300);
18389
- const [selectedCollection, setSelectedCollection] = React.useState("");
18390
- const [allItems, setAllItems] = React.useState([]);
18391
- const [nextCursor, setNextCursor] = React.useState();
18392
- const [isLoadingMore, setIsLoadingMore] = React.useState(false);
18639
+ const [selectedCollection, setSelectedCollection] = React$1.useState("");
18640
+ const [allItems, setAllItems] = React$1.useState([]);
18641
+ const [nextCursor, setNextCursor] = React$1.useState();
18642
+ const [isLoadingMore, setIsLoadingMore] = React$1.useState(false);
18393
18643
  const { data: collections = [] } = useQuery({
18394
18644
  queryKey: ["collections"],
18395
18645
  queryFn: fetchCollections,
18396
18646
  enabled: open
18397
18647
  });
18398
- React.useEffect(() => {
18648
+ React$1.useEffect(() => {
18399
18649
  if (collections.length > 0 && !selectedCollection) setSelectedCollection(collections[0].slug);
18400
18650
  }, [collections, selectedCollection]);
18401
18651
  const { data: contentResult, isLoading: contentLoading } = useQuery({
@@ -18407,7 +18657,7 @@ function ContentPickerModal({ open, onOpenChange, onSelect }) {
18407
18657
  queryFn: () => fetchContentList(selectedCollection, { limit: 50 }),
18408
18658
  enabled: open && !!selectedCollection
18409
18659
  });
18410
- React.useEffect(() => {
18660
+ React$1.useEffect(() => {
18411
18661
  if (contentResult) {
18412
18662
  setAllItems(contentResult.items);
18413
18663
  setNextCursor(contentResult.nextCursor);
@@ -18427,12 +18677,12 @@ function ContentPickerModal({ open, onOpenChange, onSelect }) {
18427
18677
  setIsLoadingMore(false);
18428
18678
  }
18429
18679
  };
18430
- const filteredItems = React.useMemo(() => {
18680
+ const filteredItems = React$1.useMemo(() => {
18431
18681
  if (!debouncedSearch) return allItems;
18432
18682
  const query = debouncedSearch.toLowerCase();
18433
18683
  return allItems.filter((item) => getItemTitle(item).toLowerCase().includes(query));
18434
18684
  }, [allItems, debouncedSearch]);
18435
- React.useEffect(() => {
18685
+ React$1.useEffect(() => {
18436
18686
  if (open) {
18437
18687
  setSearchQuery("");
18438
18688
  setSelectedCollection("");
@@ -18631,25 +18881,85 @@ function ContentPickerModal({ open, onOpenChange, onSelect }) {
18631
18881
  function MenuEditor() {
18632
18882
  const { _: _t } = useLingui();
18633
18883
  const { name } = useParams$1({ from: "/_admin/menus/$name" });
18884
+ const routeLocale = useSearch({ from: "/_admin/menus/$name" }).locale;
18634
18885
  const navigate = useNavigate$1();
18635
18886
  const queryClient = useQueryClient();
18636
18887
  const toastManager = Toast.useToastManager();
18637
- const [isAddOpen, setIsAddOpen] = React.useState(false);
18638
- const [isContentPickerOpen, setIsContentPickerOpen] = React.useState(false);
18639
- const [editingItem, setEditingItem] = React.useState(null);
18640
- const [localItems, setLocalItems] = React.useState([]);
18641
- const [addError, setAddError] = React.useState(null);
18642
- const [editError, setEditError] = React.useState(null);
18888
+ const [isAddOpen, setIsAddOpen] = React$1.useState(false);
18889
+ const [isContentPickerOpen, setIsContentPickerOpen] = React$1.useState(false);
18890
+ const [editingItem, setEditingItem] = React$1.useState(null);
18891
+ const [localItems, setLocalItems] = React$1.useState([]);
18892
+ const [addError, setAddError] = React$1.useState(null);
18893
+ const [editError, setEditError] = React$1.useState(null);
18894
+ const { data: manifest } = useQuery({
18895
+ queryKey: ["manifest"],
18896
+ queryFn: fetchManifest
18897
+ });
18898
+ const i18n = useI18nConfig(manifest);
18643
18899
  const { data: menu, isLoading } = useQuery({
18644
- queryKey: ["menu", name],
18645
- queryFn: () => fetchMenu(name),
18900
+ queryKey: [
18901
+ "menu",
18902
+ name,
18903
+ routeLocale ?? null
18904
+ ],
18905
+ queryFn: () => fetchMenu(name, { locale: routeLocale }),
18646
18906
  staleTime: Infinity
18647
18907
  });
18648
- React.useEffect(() => {
18908
+ const menuLocale = routeLocale ?? menu?.locale;
18909
+ const { data: translationsData } = useQuery({
18910
+ queryKey: [
18911
+ "menu-translations",
18912
+ name,
18913
+ menuLocale ?? null
18914
+ ],
18915
+ queryFn: () => fetchMenuTranslations(name, { locale: menuLocale }),
18916
+ enabled: !!menu && !!i18n && i18n.locales.length > 1
18917
+ });
18918
+ const translateMutation = useMutation({
18919
+ mutationFn: (targetLocale) => createMenuTranslation(name, {
18920
+ locale: targetLocale,
18921
+ label: menu?.label
18922
+ }, { locale: menuLocale }),
18923
+ onSuccess: (translated) => {
18924
+ queryClient.invalidateQueries({ queryKey: ["menus"] });
18925
+ queryClient.invalidateQueries({ queryKey: ["menu", name] });
18926
+ queryClient.invalidateQueries({ queryKey: ["menu-translations", name] });
18927
+ toastManager.add({
18928
+ title: _t({
18929
+ id: "pBdGCu",
18930
+ message: "Translation created"
18931
+ }),
18932
+ description: _t({
18933
+ id: "RKdeOp",
18934
+ message: "Menu \"{0}\" ({1}) created.",
18935
+ values: {
18936
+ 0: translated.label,
18937
+ 1: translated.locale.toUpperCase()
18938
+ }
18939
+ })
18940
+ });
18941
+ navigate({
18942
+ to: "/menus/$name",
18943
+ params: { name },
18944
+ search: { locale: translated.locale }
18945
+ });
18946
+ },
18947
+ onError: (error) => {
18948
+ toastManager.add({
18949
+ title: _t({
18950
+ id: "SlfejT",
18951
+ message: "Error"
18952
+ }),
18953
+ description: error.message,
18954
+ type: "error"
18955
+ });
18956
+ }
18957
+ });
18958
+ React$1.useEffect(() => {
18649
18959
  if (menu?.items) setLocalItems(menu.items);
18650
18960
  }, [menu]);
18651
18961
  const createMutation = useMutation({
18652
- mutationFn: (input) => createMenuItem(name, input),
18962
+ mutationFn: (input) => createMenuItem(name, input, { locale: menuLocale }),
18653
18963
  onSuccess: () => {
18654
18964
  queryClient.invalidateQueries({ queryKey: ["menu", name] });
18655
18965
  setIsAddOpen(false);
@@ -18669,7 +18979,7 @@ function MenuEditor() {
18669
18979
  }
18670
18980
  });
18671
18981
  const deleteMutation = useMutation({
18672
- mutationFn: (itemId) => deleteMenuItem(name, itemId),
18982
+ mutationFn: (itemId) => deleteMenuItem(name, itemId, { locale: menuLocale }),
18673
18983
  onSuccess: () => {
18674
18984
  queryClient.invalidateQueries({ queryKey: ["menu", name] });
18675
18985
  toastManager.add({
@@ -18695,7 +19005,7 @@ function MenuEditor() {
18695
19005
  }
18696
19006
  });
18697
19007
  const updateMutation = useMutation({
18698
- mutationFn: ({ itemId, input }) => updateMenuItem(name, itemId, input),
19008
+ mutationFn: ({ itemId, input }) => updateMenuItem(name, itemId, input, { locale: menuLocale }),
18699
19009
  onSuccess: () => {
18700
19010
  queryClient.invalidateQueries({ queryKey: ["menu", name] });
18701
19011
  setEditingItem(null);
@@ -18715,7 +19025,7 @@ function MenuEditor() {
18715
19025
  }
18716
19026
  });
18717
19027
  const reorderMutation = useMutation({
18718
- mutationFn: (input) => reorderMenuItems(name, input),
19028
+ mutationFn: (input) => reorderMenuItems(name, input, { locale: menuLocale }),
18719
19029
  onSuccess: () => {
18720
19030
  queryClient.invalidateQueries({ queryKey: ["menu", name] });
18721
19031
  toastManager.add({
@@ -18998,6 +19308,28 @@ function MenuEditor() {
18998
19308
  onOpenChange: setIsContentPickerOpen,
18999
19309
  onSelect: handleAddContent
19000
19310
  }),
19311
+ i18n && i18n.locales.length > 1 && menu ? /* @__PURE__ */ jsx("div", {
19312
+ className: "border rounded-lg p-4",
19313
+ children: /* @__PURE__ */ jsx(TranslationsPanel, {
19314
+ locales: i18n.locales,
19315
+ defaultLocale: i18n.defaultLocale,
19316
+ currentLocale: menu.locale,
19317
+ translations: translationsData?.translations.map((tr) => ({
19318
+ id: tr.id,
19319
+ locale: tr.locale
19320
+ })) ?? [{
19321
+ id: menu.id,
19322
+ locale: menu.locale
19323
+ }],
19324
+ onOpen: (tr) => navigate({
19325
+ to: "/menus/$name",
19326
+ params: { name },
19327
+ search: { locale: tr.locale }
19328
+ }),
19329
+ onCreate: (target) => translateMutation.mutate(target),
19330
+ pendingLocale: translateMutation.isPending ? translateMutation.variables ?? null : null
19331
+ })
19332
+ }) : null,
19001
19333
  localItems.length === 0 ? /* @__PURE__ */ jsxs("div", {
19002
19334
  className: "border rounded-lg p-12 text-center",
19003
19335
  children: [
@@ -19247,12 +19579,21 @@ function MenuList() {
19247
19579
  const queryClient = useQueryClient();
19248
19580
  const navigate = useNavigate$1();
19249
19581
  const toastManager = Toast.useToastManager();
19250
- const [isCreateOpen, setIsCreateOpen] = React.useState(false);
19251
- const [deleteMenuName, setDeleteMenuName] = React.useState(null);
19252
- const [createError, setCreateError] = React.useState(null);
19582
+ const [isCreateOpen, setIsCreateOpen] = React$1.useState(false);
19583
+ const [deleteMenuName, setDeleteMenuName] = React$1.useState(null);
19584
+ const [createError, setCreateError] = React$1.useState(null);
19585
+ const { data: manifest } = useQuery({
19586
+ queryKey: ["manifest"],
19587
+ queryFn: fetchManifest
19588
+ });
19589
+ const i18n = useI18nConfig(manifest);
19590
+ const [activeLocale, setActiveLocale] = React$1.useState(void 0);
19591
+ React$1.useEffect(() => {
19592
+ if (i18n && !activeLocale) setActiveLocale(i18n.defaultLocale);
19593
+ }, [i18n, activeLocale]);
19253
19594
  const { data: menus, isLoading } = useQuery({
19254
- queryKey: ["menus"],
19255
- queryFn: fetchMenus
19595
+ queryKey: ["menus", activeLocale],
19596
+ queryFn: () => fetchMenus({ locale: activeLocale })
19256
19597
  });
19257
19598
  const createMutation = useMutation({
19258
19599
  mutationFn: createMenu,
@@ -19272,7 +19613,8 @@ function MenuList() {
19272
19613
  });
19273
19614
  navigate({
19274
19615
  to: "/menus/$name",
19275
- params: { name: menu.name }
19616
+ params: { name: menu.name },
19617
+ search: { locale: menu.locale }
19276
19618
  });
19277
19619
  },
19278
19620
  onError: (error) => {
@@ -19280,7 +19622,7 @@ function MenuList() {
19280
19622
  }
19281
19623
  });
19282
19624
  const deleteMutation = useMutation({
19283
- mutationFn: deleteMenu,
19625
+ mutationFn: (name) => deleteMenu(name, { locale: activeLocale }),
19284
19626
  onSuccess: () => {
19285
19627
  queryClient.invalidateQueries({ queryKey: ["menus"] });
19286
19628
  setDeleteMenuName(null);
@@ -19306,7 +19648,8 @@ function MenuList() {
19306
19648
  const label = typeof labelVal === "string" ? labelVal : "";
19307
19649
  createMutation.mutate({
19308
19650
  name,
19309
- label
19651
+ label,
19652
+ locale: activeLocale
19310
19653
  });
19311
19654
  };
19312
19655
  if (isLoading) return /* @__PURE__ */ jsx("div", {
@@ -19323,135 +19666,147 @@ function MenuList() {
19323
19666
  className: "space-y-6",
19324
19667
  children: [
19325
19668
  /* @__PURE__ */ jsxs("div", {
19326
- className: "flex items-center justify-between",
19327
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
19328
- className: "text-3xl font-bold",
19329
- children: _t({
19330
- id: "NXjnVQ",
19331
- message: "Menus"
19332
- })
19333
- }), /* @__PURE__ */ jsx("p", {
19334
- className: "text-kumo-subtle",
19335
- children: _t({
19336
- id: "+2SUVL",
19337
- message: "Manage navigation menus for your site"
19338
- })
19339
- })] }), /* @__PURE__ */ jsxs(Dialog.Root, {
19340
- open: isCreateOpen,
19341
- onOpenChange: (open) => {
19342
- setIsCreateOpen(open);
19343
- if (!open) setCreateError(null);
19344
- },
19345
- children: [/* @__PURE__ */ jsx(Dialog.Trigger, { render: (props) => /* @__PURE__ */ jsx(Button, {
19346
- ...props,
19347
- icon: /* @__PURE__ */ jsx(Plus, {}),
19669
+ className: "flex items-center justify-between gap-4 flex-wrap",
19670
+ children: [
19671
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
19672
+ className: "text-3xl font-bold",
19348
19673
  children: _t({
19349
- id: "89ONdL",
19350
- message: "Create Menu"
19674
+ id: "NXjnVQ",
19675
+ message: "Menus"
19351
19676
  })
19352
- }) }), /* @__PURE__ */ jsxs(Dialog, {
19353
- className: "p-6",
19354
- size: "lg",
19355
- children: [/* @__PURE__ */ jsxs("div", {
19356
- className: "flex items-start justify-between gap-4 mb-4",
19357
- children: [/* @__PURE__ */ jsx(Dialog.Title, {
19358
- className: "text-lg font-semibold leading-none tracking-tight",
19359
- children: _t({
19360
- id: "7P7UXw",
19361
- message: "Create New Menu"
19362
- })
19363
- }), /* @__PURE__ */ jsx(Dialog.Close, {
19364
- "aria-label": _t({
19365
- id: "yz7wBu",
19366
- message: "Close"
19367
- }),
19368
- render: (props) => /* @__PURE__ */ jsxs(Button, {
19369
- ...props,
19370
- variant: "ghost",
19371
- shape: "square",
19677
+ }), /* @__PURE__ */ jsx("p", {
19678
+ className: "text-kumo-subtle",
19679
+ children: _t({
19680
+ id: "+2SUVL",
19681
+ message: "Manage navigation menus for your site"
19682
+ })
19683
+ })] }),
19684
+ /* @__PURE__ */ jsx("div", {
19685
+ className: "flex items-center gap-2",
19686
+ children: i18n && activeLocale ? /* @__PURE__ */ jsx(LocaleSwitcher, {
19687
+ locales: i18n.locales,
19688
+ defaultLocale: i18n.defaultLocale,
19689
+ value: activeLocale,
19690
+ onChange: setActiveLocale
19691
+ }) : null
19692
+ }),
19693
+ /* @__PURE__ */ jsxs(Dialog.Root, {
19694
+ open: isCreateOpen,
19695
+ onOpenChange: (open) => {
19696
+ setIsCreateOpen(open);
19697
+ if (!open) setCreateError(null);
19698
+ },
19699
+ children: [/* @__PURE__ */ jsx(Dialog.Trigger, { render: (props) => /* @__PURE__ */ jsx(Button, {
19700
+ ...props,
19701
+ icon: /* @__PURE__ */ jsx(Plus, {}),
19702
+ children: _t({
19703
+ id: "89ONdL",
19704
+ message: "Create Menu"
19705
+ })
19706
+ }) }), /* @__PURE__ */ jsxs(Dialog, {
19707
+ className: "p-6",
19708
+ size: "lg",
19709
+ children: [/* @__PURE__ */ jsxs("div", {
19710
+ className: "flex items-start justify-between gap-4 mb-4",
19711
+ children: [/* @__PURE__ */ jsx(Dialog.Title, {
19712
+ className: "text-lg font-semibold leading-none tracking-tight",
19713
+ children: _t({
19714
+ id: "7P7UXw",
19715
+ message: "Create New Menu"
19716
+ })
19717
+ }), /* @__PURE__ */ jsx(Dialog.Close, {
19372
19718
  "aria-label": _t({
19373
19719
  id: "yz7wBu",
19374
19720
  message: "Close"
19375
19721
  }),
19376
- className: "absolute end-4 top-4",
19377
- children: [/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", {
19378
- className: "sr-only",
19379
- children: _t({
19722
+ render: (props) => /* @__PURE__ */ jsxs(Button, {
19723
+ ...props,
19724
+ variant: "ghost",
19725
+ shape: "square",
19726
+ "aria-label": _t({
19380
19727
  id: "yz7wBu",
19381
19728
  message: "Close"
19382
- })
19383
- })]
19384
- })
19385
- })]
19386
- }), /* @__PURE__ */ jsxs("form", {
19387
- onSubmit: handleCreate,
19388
- className: "space-y-4",
19389
- children: [
19390
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
19391
- label: _t({
19392
- id: "6YtxFj",
19393
- message: "Name"
19394
- }),
19395
- name: "name",
19396
- required: true,
19397
- placeholder: "primary",
19398
- pattern: "[a-z0-9\\-]+",
19399
- title: _t({
19400
- id: "6ZQzu/",
19401
- message: "Only lowercase letters, numbers, and hyphens"
19402
- })
19403
- }), /* @__PURE__ */ jsx("p", {
19404
- className: "text-sm text-kumo-subtle mt-1",
19405
- children: _t({
19406
- id: "P5zbwF",
19407
- message: "URL-friendly identifier (e.g., \"primary\", \"footer\")"
19408
- })
19409
- })] }),
19410
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
19411
- label: _t({
19412
- id: "87a/t/",
19413
- message: "Label"
19414
- }),
19415
- name: "label",
19416
- required: true,
19417
- placeholder: _t({
19418
- id: "9xZvwS",
19419
- message: "Primary Navigation"
19420
- })
19421
- }), /* @__PURE__ */ jsx("p", {
19422
- className: "text-sm text-kumo-subtle mt-1",
19423
- children: _t({
19424
- id: "1jYgRd",
19425
- message: "Display name for admin interface"
19729
+ }),
19730
+ className: "absolute end-4 top-4",
19731
+ children: [/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", {
19732
+ className: "sr-only",
19733
+ children: _t({
19734
+ id: "yz7wBu",
19735
+ message: "Close"
19736
+ })
19737
+ })]
19426
19738
  })
19427
- })] }),
19428
- /* @__PURE__ */ jsx(DialogError, { message: createError || getMutationError(createMutation.error) }),
19429
- /* @__PURE__ */ jsxs("div", {
19430
- className: "flex justify-end gap-2",
19431
- children: [/* @__PURE__ */ jsx(Button, {
19432
- type: "button",
19433
- variant: "outline",
19434
- onClick: () => setIsCreateOpen(false),
19739
+ })]
19740
+ }), /* @__PURE__ */ jsxs("form", {
19741
+ onSubmit: handleCreate,
19742
+ className: "space-y-4",
19743
+ children: [
19744
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
19745
+ label: _t({
19746
+ id: "6YtxFj",
19747
+ message: "Name"
19748
+ }),
19749
+ name: "name",
19750
+ required: true,
19751
+ placeholder: "primary",
19752
+ pattern: "[a-z0-9\\-]+",
19753
+ title: _t({
19754
+ id: "6ZQzu/",
19755
+ message: "Only lowercase letters, numbers, and hyphens"
19756
+ })
19757
+ }), /* @__PURE__ */ jsx("p", {
19758
+ className: "text-sm text-kumo-subtle mt-1",
19435
19759
  children: _t({
19436
- id: "dEgA5A",
19437
- message: "Cancel"
19760
+ id: "P5zbwF",
19761
+ message: "URL-friendly identifier (e.g., \"primary\", \"footer\")"
19438
19762
  })
19439
- }), /* @__PURE__ */ jsx(Button, {
19440
- type: "submit",
19441
- disabled: createMutation.isPending,
19442
- children: createMutation.isPending ? _t({
19443
- id: "HM56Bx",
19444
- message: "Creating..."
19445
- }) : _t({
19446
- id: "hYgDIe",
19447
- message: "Create"
19763
+ })] }),
19764
+ /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
19765
+ label: _t({
19766
+ id: "87a/t/",
19767
+ message: "Label"
19768
+ }),
19769
+ name: "label",
19770
+ required: true,
19771
+ placeholder: _t({
19772
+ id: "9xZvwS",
19773
+ message: "Primary Navigation"
19448
19774
  })
19449
- })]
19450
- })
19451
- ]
19775
+ }), /* @__PURE__ */ jsx("p", {
19776
+ className: "text-sm text-kumo-subtle mt-1",
19777
+ children: _t({
19778
+ id: "1jYgRd",
19779
+ message: "Display name for admin interface"
19780
+ })
19781
+ })] }),
19782
+ /* @__PURE__ */ jsx(DialogError, { message: createError || getMutationError(createMutation.error) }),
19783
+ /* @__PURE__ */ jsxs("div", {
19784
+ className: "flex justify-end gap-2",
19785
+ children: [/* @__PURE__ */ jsx(Button, {
19786
+ type: "button",
19787
+ variant: "outline",
19788
+ onClick: () => setIsCreateOpen(false),
19789
+ children: _t({
19790
+ id: "dEgA5A",
19791
+ message: "Cancel"
19792
+ })
19793
+ }), /* @__PURE__ */ jsx(Button, {
19794
+ type: "submit",
19795
+ disabled: createMutation.isPending,
19796
+ children: createMutation.isPending ? _t({
19797
+ id: "HM56Bx",
19798
+ message: "Creating..."
19799
+ }) : _t({
19800
+ id: "hYgDIe",
19801
+ message: "Create"
19802
+ })
19803
+ })]
19804
+ })
19805
+ ]
19806
+ })]
19452
19807
  })]
19453
- })]
19454
- })]
19808
+ })
19809
+ ]
19455
19810
  }),
19456
19811
  !menus || menus.length === 0 ? /* @__PURE__ */ jsxs("div", {
19457
19812
  className: "border rounded-lg p-12 text-center",
@@ -19487,10 +19842,14 @@ function MenuList() {
19487
19842
  children: [/* @__PURE__ */ jsx(Link$1, {
19488
19843
  to: "/menus/$name",
19489
19844
  params: { name: menu.name },
19845
+ search: { locale: menu.locale },
19490
19846
  className: "flex-1",
19491
- children: /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h3", {
19847
+ children: /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("h3", {
19492
19848
  className: "font-semibold text-lg",
19493
- children: menu.label
19849
+ children: [menu.label, i18n ? /* @__PURE__ */ jsx("span", {
19850
+ className: "ms-2 text-xs font-mono uppercase text-kumo-subtle",
19851
+ children: menu.locale
19852
+ }) : null]
19494
19853
  }), /* @__PURE__ */ jsxs("p", {
19495
19854
  className: "text-sm text-kumo-subtle",
19496
19855
  children: [
@@ -19505,6 +19864,7 @@ function MenuList() {
19505
19864
  children: [/* @__PURE__ */ jsxs(Link$1, {
19506
19865
  to: "/menus/$name",
19507
19866
  params: { name: menu.name },
19867
+ search: { locale: menu.locale },
19508
19868
  className: buttonVariants({
19509
19869
  variant: "outline",
19510
19870
  size: "sm"
@@ -20444,27 +20804,27 @@ function SectionEditor() {
20444
20804
  }
20445
20805
  function SectionEditorForm({ section, isSaving, onSave }) {
20446
20806
  const { _: _t2 } = useLingui();
20447
- const [title, setTitle] = React.useState(section.title);
20448
- const [sectionSlug, setSectionSlug] = React.useState(section.slug);
20449
- const [slugTouched, setSlugTouched] = React.useState(true);
20450
- const [description, setDescription] = React.useState(section.description || "");
20451
- const [keywords, setKeywords] = React.useState(section.keywords.join(", "));
20452
- const [content, setContent] = React.useState(section.content);
20453
- const [lastSavedData] = React.useState(() => JSON.stringify({
20807
+ const [title, setTitle] = React$1.useState(section.title);
20808
+ const [sectionSlug, setSectionSlug] = React$1.useState(section.slug);
20809
+ const [slugTouched, setSlugTouched] = React$1.useState(true);
20810
+ const [description, setDescription] = React$1.useState(section.description || "");
20811
+ const [keywords, setKeywords] = React$1.useState(section.keywords.join(", "));
20812
+ const [content, setContent] = React$1.useState(section.content);
20813
+ const [lastSavedData] = React$1.useState(() => JSON.stringify({
20454
20814
  title: section.title,
20455
20815
  slug: section.slug,
20456
20816
  description: section.description || "",
20457
20817
  keywords: section.keywords.join(", "),
20458
20818
  content: section.content
20459
20819
  }));
20460
- React.useEffect(() => {
20820
+ React$1.useEffect(() => {
20461
20821
  if (!slugTouched && title && title !== section.title) setSectionSlug(slugify(title));
20462
20822
  }, [
20463
20823
  title,
20464
20824
  slugTouched,
20465
20825
  section.title
20466
20826
  ]);
20467
- const isDirty = React.useMemo(() => JSON.stringify({
20827
+ const isDirty = React$1.useMemo(() => JSON.stringify({
20468
20828
  title,
20469
20829
  slug: sectionSlug,
20470
20830
  description,
@@ -20477,11 +20837,11 @@ function SectionEditorForm({ section, isSaving, onSave }) {
20477
20837
  keywords,
20478
20838
  content
20479
20839
  ]) !== lastSavedData;
20480
- const [blockSidebarPanel, setBlockSidebarPanel] = React.useState(null);
20481
- const handleBlockSidebarOpen = React.useCallback((panel) => {
20840
+ const [blockSidebarPanel, setBlockSidebarPanel] = React$1.useState(null);
20841
+ const handleBlockSidebarOpen = React$1.useCallback((panel) => {
20482
20842
  setBlockSidebarPanel(panel);
20483
20843
  }, []);
20484
- const handleBlockSidebarClose = React.useCallback(() => {
20844
+ const handleBlockSidebarClose = React$1.useCallback(() => {
20485
20845
  setBlockSidebarPanel((prev) => {
20486
20846
  prev?.onClose();
20487
20847
  return null;
@@ -20700,16 +21060,16 @@ function Sections() {
20700
21060
  const navigate = useNavigate$1();
20701
21061
  const queryClient = useQueryClient();
20702
21062
  const toastManager = Toast.useToastManager();
20703
- const [isCreateOpen, setIsCreateOpen] = React.useState(false);
20704
- const [deleteSlug, setDeleteSlug] = React.useState(null);
20705
- const [searchQuery, setSearchQuery] = React.useState("");
20706
- const [selectedSource, setSelectedSource] = React.useState(null);
20707
- const [createTitle, setCreateTitle] = React.useState("");
20708
- const [createSlug, setCreateSlug] = React.useState("");
20709
- const [createDescription, setCreateDescription] = React.useState("");
20710
- const [slugTouched, setSlugTouched] = React.useState(false);
20711
- const [createError, setCreateError] = React.useState(null);
20712
- React.useEffect(() => {
21063
+ const [isCreateOpen, setIsCreateOpen] = React$1.useState(false);
21064
+ const [deleteSlug, setDeleteSlug] = React$1.useState(null);
21065
+ const [searchQuery, setSearchQuery] = React$1.useState("");
21066
+ const [selectedSource, setSelectedSource] = React$1.useState(null);
21067
+ const [createTitle, setCreateTitle] = React$1.useState("");
21068
+ const [createSlug, setCreateSlug] = React$1.useState("");
21069
+ const [createDescription, setCreateDescription] = React$1.useState("");
21070
+ const [slugTouched, setSlugTouched] = React$1.useState(false);
21071
+ const [createError, setCreateError] = React$1.useState(null);
21072
+ React$1.useEffect(() => {
20713
21073
  if (!isCreateOpen) {
20714
21074
  setCreateTitle("");
20715
21075
  setCreateSlug("");
@@ -21454,15 +21814,15 @@ const MSG_ROLE_UNKNOWN = {
21454
21814
  */
21455
21815
  function useRolesConfig() {
21456
21816
  const { _: _t } = useLingui();
21457
- const roles = React.useMemo(() => ROLE_ENTRIES.map(({ value, label, description }) => ({
21817
+ const roles = React$1.useMemo(() => ROLE_ENTRIES.map(({ value, label, description }) => ({
21458
21818
  value,
21459
21819
  label: _t(label),
21460
21820
  description: _t(description)
21461
21821
  })), [_t]);
21462
- const roleLabels = React.useMemo(() => Object.fromEntries(ROLE_ENTRIES.map((r) => [String(r.value), _t(r.label)])), [_t]);
21822
+ const roleLabels = React$1.useMemo(() => Object.fromEntries(ROLE_ENTRIES.map((r) => [String(r.value), _t(r.label)])), [_t]);
21463
21823
  return {
21464
21824
  roleLabels,
21465
- getRoleLabel: React.useCallback((level) => roleLabels[String(level)] ?? _t(MSG_ROLE_UNKNOWN), [roleLabels, _t]),
21825
+ getRoleLabel: React$1.useCallback((level) => roleLabels[String(level)] ?? _t(MSG_ROLE_UNKNOWN), [roleLabels, _t]),
21466
21826
  roles
21467
21827
  };
21468
21828
  }
@@ -21477,11 +21837,11 @@ const MAX_SELF_SIGNUP_DEFAULT_ROLE = 40;
21477
21837
  */
21478
21838
  function useAllowedDomainsRolesConfig() {
21479
21839
  const { roleLabels, getRoleLabel, roles } = useRolesConfig();
21480
- const signupRoles = React.useMemo(() => roles.filter((r) => r.value <= MAX_SELF_SIGNUP_DEFAULT_ROLE), [roles]);
21840
+ const signupRoles = React$1.useMemo(() => roles.filter((r) => r.value <= MAX_SELF_SIGNUP_DEFAULT_ROLE), [roles]);
21481
21841
  return {
21482
21842
  getRoleLabel,
21483
21843
  signupRoles,
21484
- signupRoleItems: React.useMemo(() => {
21844
+ signupRoleItems: React$1.useMemo(() => {
21485
21845
  const entries = signupRoles.map((r) => {
21486
21846
  const label = roleLabels[String(r.value)];
21487
21847
  return [String(r.value), label ?? getRoleLabel(r.value)];
@@ -21507,12 +21867,12 @@ function AllowedDomainsSettings() {
21507
21867
  const { _: _t } = useLingui();
21508
21868
  const { getRoleLabel, signupRoles, signupRoleItems } = useAllowedDomainsRolesConfig();
21509
21869
  const queryClient = useQueryClient();
21510
- const [isAddingDomain, setIsAddingDomain] = React.useState(false);
21511
- const [editingDomain, setEditingDomain] = React.useState(null);
21512
- const [deletingDomain, setDeletingDomain] = React.useState(null);
21513
- const [saveStatus, setSaveStatus] = React.useState(null);
21514
- const [newDomain, setNewDomain] = React.useState("");
21515
- const [newRole, setNewRole] = React.useState(30);
21870
+ const [isAddingDomain, setIsAddingDomain] = React$1.useState(false);
21871
+ const [editingDomain, setEditingDomain] = React$1.useState(null);
21872
+ const [deletingDomain, setDeletingDomain] = React$1.useState(null);
21873
+ const [saveStatus, setSaveStatus] = React$1.useState(null);
21874
+ const [newDomain, setNewDomain] = React$1.useState("");
21875
+ const [newRole, setNewRole] = React$1.useState(30);
21516
21876
  const { data: manifest, isLoading: manifestLoading } = useQuery({
21517
21877
  queryKey: ["manifest"],
21518
21878
  queryFn: fetchManifest
@@ -21523,7 +21883,7 @@ function AllowedDomainsSettings() {
21523
21883
  queryFn: fetchAllowedDomains,
21524
21884
  enabled: !isExternalAuth && !manifestLoading
21525
21885
  });
21526
- React.useEffect(() => {
21886
+ React$1.useEffect(() => {
21527
21887
  if (saveStatus) {
21528
21888
  const timer = setTimeout(setSaveStatus, 3e3, null);
21529
21889
  return () => clearTimeout(timer);
@@ -22160,11 +22520,11 @@ function computeExpiryDate(option) {
22160
22520
  function ApiTokenSettings() {
22161
22521
  const { _: _t } = useLingui();
22162
22522
  const queryClient = useQueryClient();
22163
- const [showCreateForm, setShowCreateForm] = React.useState(false);
22164
- const [newToken, setNewToken] = React.useState(null);
22165
- const [tokenVisible, setTokenVisible] = React.useState(false);
22166
- const [copied, setCopied] = React.useState(false);
22167
- const [revokeConfirmId, setRevokeConfirmId] = React.useState(null);
22523
+ const [showCreateForm, setShowCreateForm] = React$1.useState(false);
22524
+ const [newToken, setNewToken] = React$1.useState(null);
22525
+ const [tokenVisible, setTokenVisible] = React$1.useState(false);
22526
+ const [copied, setCopied] = React$1.useState(false);
22527
+ const [revokeConfirmId, setRevokeConfirmId] = React$1.useState(null);
22168
22528
  const { data: tokens, isLoading } = useQuery({
22169
22529
  queryKey: ["api-tokens"],
22170
22530
  queryFn: fetchApiTokens
@@ -22186,8 +22546,8 @@ function ApiTokenSettings() {
22186
22546
  queryClient.invalidateQueries({ queryKey: ["api-tokens"] });
22187
22547
  }
22188
22548
  });
22189
- const copyTimeoutRef = React.useRef(void 0);
22190
- React.useEffect(() => {
22549
+ const copyTimeoutRef = React$1.useRef(void 0);
22550
+ React$1.useEffect(() => {
22191
22551
  return () => {
22192
22552
  if (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);
22193
22553
  };
@@ -22200,7 +22560,7 @@ function ApiTokenSettings() {
22200
22560
  copyTimeoutRef.current = setTimeout(setCopied, 2e3, false);
22201
22561
  } catch {}
22202
22562
  };
22203
- const expirySelectItems = React.useMemo(() => Object.fromEntries(EXPIRY_OPTIONS.map((o) => [o.value, _t(o.label)])), [_t]);
22563
+ const expirySelectItems = React$1.useMemo(() => Object.fromEntries(EXPIRY_OPTIONS.map((o) => [o.value, _t(o.label)])), [_t]);
22204
22564
  return /* @__PURE__ */ jsxs("div", {
22205
22565
  className: "space-y-6",
22206
22566
  children: [
@@ -22445,9 +22805,9 @@ function ApiTokenSettings() {
22445
22805
  }
22446
22806
  function CreateTokenForm({ expirySelectItems, isCreating, error, onSubmit, onCancel }) {
22447
22807
  const { _: _t2 } = useLingui();
22448
- const [name, setName] = React.useState("");
22449
- const [selectedScopes, setSelectedScopes] = React.useState(/* @__PURE__ */ new Set());
22450
- const [expiry, setExpiry] = React.useState("30d");
22808
+ const [name, setName] = React$1.useState("");
22809
+ const [selectedScopes, setSelectedScopes] = React$1.useState(/* @__PURE__ */ new Set());
22810
+ const [expiry, setExpiry] = React$1.useState("30d");
22451
22811
  const toggleScope = (scope) => {
22452
22812
  setSelectedScopes((prev) => {
22453
22813
  const next = new Set(prev);
@@ -22572,9 +22932,9 @@ function CreateTokenForm({ expirySelectItems, isCreating, error, onSubmit, onCan
22572
22932
  */
22573
22933
  function EmailSettings() {
22574
22934
  const { _: _t } = useLingui();
22575
- const [testEmail, setTestEmail] = React.useState("");
22576
- const [status, setStatus] = React.useState(null);
22577
- React.useEffect(() => {
22935
+ const [testEmail, setTestEmail] = React$1.useState("");
22936
+ const [status, setStatus] = React$1.useState(null);
22937
+ React$1.useEffect(() => {
22578
22938
  if (!status) return;
22579
22939
  const timer = setTimeout(setStatus, 5e3, null);
22580
22940
  return () => clearTimeout(timer);
@@ -22866,14 +23226,14 @@ function GeneralSettings() {
22866
23226
  queryFn: fetchSettings,
22867
23227
  staleTime: Infinity
22868
23228
  });
22869
- const [formData, setFormData] = React.useState({});
22870
- const [saveStatus, setSaveStatus] = React.useState(null);
22871
- const [logoPickerOpen, setLogoPickerOpen] = React.useState(false);
22872
- const [faviconPickerOpen, setFaviconPickerOpen] = React.useState(false);
22873
- React.useEffect(() => {
23229
+ const [formData, setFormData] = React$1.useState({});
23230
+ const [saveStatus, setSaveStatus] = React$1.useState(null);
23231
+ const [logoPickerOpen, setLogoPickerOpen] = React$1.useState(false);
23232
+ const [faviconPickerOpen, setFaviconPickerOpen] = React$1.useState(false);
23233
+ React$1.useEffect(() => {
22874
23234
  if (settings) setFormData(settings);
22875
23235
  }, [settings]);
22876
- React.useEffect(() => {
23236
+ React$1.useEffect(() => {
22877
23237
  if (saveStatus) {
22878
23238
  const timer = setTimeout(setSaveStatus, 3e3, null);
22879
23239
  return () => clearTimeout(timer);
@@ -23284,12 +23644,12 @@ function formatRelativeTime(dateString) {
23284
23644
  }
23285
23645
  function PasskeyItem({ passkey, canDelete, onRename, onDelete, isDeleting, isRenaming }) {
23286
23646
  const { _: _t } = useLingui();
23287
- const [isEditing, setIsEditing] = React.useState(false);
23288
- const [editName, setEditName] = React.useState(passkey.name || "");
23289
- const [showDeleteDialog, setShowDeleteDialog] = React.useState(false);
23290
- const [deleteError, setDeleteError] = React.useState(null);
23291
- const inputRef = React.useRef(null);
23292
- React.useEffect(() => {
23647
+ const [isEditing, setIsEditing] = React$1.useState(false);
23648
+ const [editName, setEditName] = React$1.useState(passkey.name || "");
23649
+ const [showDeleteDialog, setShowDeleteDialog] = React$1.useState(false);
23650
+ const [deleteError, setDeleteError] = React$1.useState(null);
23651
+ const inputRef = React$1.useRef(null);
23652
+ React$1.useEffect(() => {
23293
23653
  if (isEditing && inputRef.current) {
23294
23654
  inputRef.current.focus();
23295
23655
  inputRef.current.select();
@@ -23509,8 +23869,8 @@ function PasskeyList({ passkeys, onRename, onDelete, isDeleting, isRenaming }) {
23509
23869
  function SecuritySettings() {
23510
23870
  const { _: _t } = useLingui();
23511
23871
  const queryClient = useQueryClient();
23512
- const [isAdding, setIsAdding] = React.useState(false);
23513
- const [saveStatus, setSaveStatus] = React.useState(null);
23872
+ const [isAdding, setIsAdding] = React$1.useState(false);
23873
+ const [saveStatus, setSaveStatus] = React$1.useState(null);
23514
23874
  const { data: manifest, isLoading: manifestLoading } = useQuery({
23515
23875
  queryKey: ["manifest"],
23516
23876
  queryFn: fetchManifest
@@ -23521,7 +23881,7 @@ function SecuritySettings() {
23521
23881
  queryFn: fetchPasskeys,
23522
23882
  enabled: !isExternalAuth && !manifestLoading
23523
23883
  });
23524
- React.useEffect(() => {
23884
+ React$1.useEffect(() => {
23525
23885
  if (saveStatus) {
23526
23886
  const timer = setTimeout(setSaveStatus, 3e3, null);
23527
23887
  return () => clearTimeout(timer);
@@ -23764,12 +24124,12 @@ function SeoSettings() {
23764
24124
  queryFn: fetchSettings,
23765
24125
  staleTime: Infinity
23766
24126
  });
23767
- const [formData, setFormData] = React.useState({});
23768
- const [saveStatus, setSaveStatus] = React.useState(null);
23769
- React.useEffect(() => {
24127
+ const [formData, setFormData] = React$1.useState({});
24128
+ const [saveStatus, setSaveStatus] = React$1.useState(null);
24129
+ React$1.useEffect(() => {
23770
24130
  if (settings) setFormData(settings);
23771
24131
  }, [settings]);
23772
- React.useEffect(() => {
24132
+ React$1.useEffect(() => {
23773
24133
  if (saveStatus) {
23774
24134
  const timer = setTimeout(setSaveStatus, 3e3, null);
23775
24135
  return () => clearTimeout(timer);
@@ -23985,12 +24345,12 @@ function SocialSettings() {
23985
24345
  queryFn: fetchSettings,
23986
24346
  staleTime: Infinity
23987
24347
  });
23988
- const [formData, setFormData] = React.useState({});
23989
- const [saveStatus, setSaveStatus] = React.useState(null);
23990
- React.useEffect(() => {
24348
+ const [formData, setFormData] = React$1.useState({});
24349
+ const [saveStatus, setSaveStatus] = React$1.useState(null);
24350
+ React$1.useEffect(() => {
23991
24351
  if (settings) setFormData(settings);
23992
24352
  }, [settings]);
23993
- React.useEffect(() => {
24353
+ React$1.useEffect(() => {
23994
24354
  if (saveStatus) {
23995
24355
  const timer = setTimeout(setSaveStatus, 3e3, null);
23996
24356
  return () => clearTimeout(timer);
@@ -24257,10 +24617,10 @@ async function executeAdminSetup(data) {
24257
24617
  }
24258
24618
  function SiteStep({ seedInfo, onNext, isLoading, error }) {
24259
24619
  const { _: _t } = useLingui();
24260
- const [title, setTitle] = React.useState(seedInfo?.title ?? "");
24261
- const [tagline, setTagline] = React.useState(seedInfo?.tagline ?? "");
24262
- const [includeContent, setIncludeContent] = React.useState(true);
24263
- const [errors, setErrors] = React.useState({});
24620
+ const [title, setTitle] = React$1.useState(seedInfo?.title ?? "");
24621
+ const [tagline, setTagline] = React$1.useState(seedInfo?.tagline ?? "");
24622
+ const [includeContent, setIncludeContent] = React$1.useState(true);
24623
+ const [errors, setErrors] = React$1.useState({});
24264
24624
  const validate = () => {
24265
24625
  const newErrors = {};
24266
24626
  if (!title.trim()) newErrors.title = _t({
@@ -24370,9 +24730,9 @@ function SiteStep({ seedInfo, onNext, isLoading, error }) {
24370
24730
  }
24371
24731
  function AdminStep({ onNext, onBack, isLoading, error }) {
24372
24732
  const { _: _t2 } = useLingui();
24373
- const [email, setEmail] = React.useState("");
24374
- const [name, setName] = React.useState("");
24375
- const [errors, setErrors] = React.useState({});
24733
+ const [email, setEmail] = React$1.useState("");
24734
+ const [name, setName] = React$1.useState("");
24735
+ const [errors, setErrors] = React$1.useState({});
24376
24736
  const validate = () => {
24377
24737
  const newErrors = {};
24378
24738
  if (!email.trim()) newErrors.email = _t2({
@@ -24475,7 +24835,7 @@ function handleSetupSuccess() {
24475
24835
  }
24476
24836
  function AuthMethodStep({ adminData, providers, onBack }) {
24477
24837
  const { _: _t3 } = useLingui();
24478
- const [activeProvider, setActiveProvider] = React.useState(null);
24838
+ const [activeProvider, setActiveProvider] = React$1.useState(null);
24479
24839
  const buttonProviders = providers.filter((p) => p.LoginButton);
24480
24840
  const hasProviders = buttonProviders.length > 0;
24481
24841
  if (activeProvider) {
@@ -24608,7 +24968,7 @@ function StepIndicator$1({ currentStep, useAccessAuth }) {
24608
24968
  const currentIndex = steps.findIndex((s) => s.key === currentStep);
24609
24969
  return /* @__PURE__ */ jsx("div", {
24610
24970
  className: "flex items-center justify-center mb-8",
24611
- children: steps.map((step, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [/* @__PURE__ */ jsxs("div", {
24971
+ children: steps.map((step, index) => /* @__PURE__ */ jsxs(React$1.Fragment, { children: [/* @__PURE__ */ jsxs("div", {
24612
24972
  className: "flex items-center",
24613
24973
  children: [/* @__PURE__ */ jsx("div", {
24614
24974
  className: `
@@ -24635,13 +24995,13 @@ function StepIndicator$1({ currentStep, useAccessAuth }) {
24635
24995
  });
24636
24996
  }
24637
24997
  function SetupWizard() {
24638
- const [currentStep, setCurrentStep] = React.useState("site");
24639
- const [_siteData, setSiteData] = React.useState(null);
24640
- const [adminData, setAdminData] = React.useState(null);
24641
- const [error, setError] = React.useState();
24642
- const [urlError, setUrlError] = React.useState(null);
24998
+ const [currentStep, setCurrentStep] = React$1.useState("site");
24999
+ const [_siteData, setSiteData] = React$1.useState(null);
25000
+ const [adminData, setAdminData] = React$1.useState(null);
25001
+ const [error, setError] = React$1.useState();
25002
+ const [urlError, setUrlError] = React$1.useState(null);
24643
25003
  const authProviderList = useAuthProviderList();
24644
- React.useEffect(() => {
25004
+ React$1.useEffect(() => {
24645
25005
  const params = new URLSearchParams(window.location.search);
24646
25006
  const errorParam = params.get("error");
24647
25007
  const message = params.get("message");
@@ -24826,8 +25186,8 @@ const IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/.test(n
24826
25186
  * Custom hook for debouncing a value
24827
25187
  */
24828
25188
  function useDebouncedValue(value, delay) {
24829
- const [debouncedValue, setDebouncedValue] = React.useState(value);
24830
- React.useEffect(() => {
25189
+ const [debouncedValue, setDebouncedValue] = React$1.useState(value);
25190
+ React$1.useEffect(() => {
24831
25191
  const timer = setTimeout(() => {
24832
25192
  setDebouncedValue(value);
24833
25193
  }, delay);
@@ -25020,8 +25380,8 @@ function filterNavItems(items, query, translate) {
25020
25380
  }
25021
25381
  function AdminCommandPalette({ manifest }) {
25022
25382
  const { _: _t } = useLingui();
25023
- const [open, setOpen] = React.useState(false);
25024
- const [query, setQuery] = React.useState("");
25383
+ const [open, setOpen] = React$1.useState(false);
25384
+ const [query, setQuery] = React$1.useState("");
25025
25385
  const navigate = useNavigate$1();
25026
25386
  const debouncedQuery = useDebouncedValue(query, SEARCH_DEBOUNCE_MS);
25027
25387
  const { data: user } = useCurrentUser();
@@ -25033,13 +25393,13 @@ function AdminCommandPalette({ manifest }) {
25033
25393
  staleTime: 30 * 1e3
25034
25394
  });
25035
25395
  const isPendingSearch = query.length >= 2 && query !== debouncedQuery || isSearching;
25036
- const allNavItems = React.useMemo(() => buildNavItems(manifest, userRole), [manifest, userRole]);
25037
- const filteredNavItems = React.useMemo(() => filterNavItems(allNavItems, query, _t), [
25396
+ const allNavItems = React$1.useMemo(() => buildNavItems(manifest, userRole), [manifest, userRole]);
25397
+ const filteredNavItems = React$1.useMemo(() => filterNavItems(allNavItems, query, _t), [
25038
25398
  allNavItems,
25039
25399
  query,
25040
25400
  _t
25041
25401
  ]);
25042
- const resultGroups = React.useMemo(() => {
25402
+ const resultGroups = React$1.useMemo(() => {
25043
25403
  const groups = [];
25044
25404
  if (filteredNavItems.length > 0) groups.push({
25045
25405
  id: "navigation",
@@ -25091,10 +25451,10 @@ function AdminCommandPalette({ manifest }) {
25091
25451
  e.preventDefault();
25092
25452
  setOpen(true);
25093
25453
  });
25094
- React.useEffect(() => {
25454
+ React$1.useEffect(() => {
25095
25455
  if (!open) setQuery("");
25096
25456
  }, [open]);
25097
- const handleSelect = React.useCallback((item, options) => {
25457
+ const handleSelect = React$1.useCallback((item, options) => {
25098
25458
  setOpen(false);
25099
25459
  if (options.newTab) {
25100
25460
  const path = item.params ? item.to.replace(ROUTE_PARAM_REGEX, (_, key) => item.params?.[key] ?? "") : item.to;
@@ -25104,7 +25464,7 @@ function AdminCommandPalette({ manifest }) {
25104
25464
  params: item.params
25105
25465
  });
25106
25466
  }, [navigate]);
25107
- const handleItemClick = React.useCallback((item, e) => {
25467
+ const handleItemClick = React$1.useCallback((item, e) => {
25108
25468
  handleSelect(item, { newTab: e.metaKey || e.ctrlKey });
25109
25469
  }, [handleSelect]);
25110
25470
  return /* @__PURE__ */ jsxs(CommandPalette.Root, {
@@ -25688,7 +26048,7 @@ async function handleLogout() {
25688
26048
  */
25689
26049
  function Header() {
25690
26050
  const { _: _t } = useLingui();
25691
- const [userMenuOpen, setUserMenuOpen] = React.useState(false);
26051
+ const [userMenuOpen, setUserMenuOpen] = React$1.useState(false);
25692
26052
  const { data: user } = useCurrentUser();
25693
26053
  const displayName = user?.name || user?.email || _t({
25694
26054
  id: "7PzzBU",
@@ -25991,9 +26351,9 @@ function WelcomeModal({ open, onClose, userName, userRole }) {
25991
26351
  * handling collapse state, mobile detection, and layout transitions.
25992
26352
  */
25993
26353
  function Shell({ children, manifest }) {
25994
- const [welcomeModalOpen, setWelcomeModalOpen] = React.useState(false);
26354
+ const [welcomeModalOpen, setWelcomeModalOpen] = React$1.useState(false);
25995
26355
  const { data: user } = useCurrentUser();
25996
- React.useEffect(() => {
26356
+ React$1.useEffect(() => {
25997
26357
  if (user?.isFirstLogin) setWelcomeModalOpen(true);
25998
26358
  }, [user?.isFirstLogin]);
25999
26359
  return /* @__PURE__ */ jsxs(KumoSidebar.Provider, {
@@ -26039,8 +26399,8 @@ function Shell({ children, manifest }) {
26039
26399
  */
26040
26400
  function EmailStep({ onSubmit, isLoading, error }) {
26041
26401
  const { _: _t } = useLingui();
26042
- const [email, setEmail] = React.useState("");
26043
- const [validationError, setValidationError] = React.useState(null);
26402
+ const [email, setEmail] = React$1.useState("");
26403
+ const [validationError, setValidationError] = React$1.useState(null);
26044
26404
  const handleSubmit = (e) => {
26045
26405
  e.preventDefault();
26046
26406
  setValidationError(null);
@@ -26200,7 +26560,7 @@ function handleSignupSuccess() {
26200
26560
  }
26201
26561
  function VerifyStep({ verifyResult, token, onBack: _onBack }) {
26202
26562
  const { _: _t3 } = useLingui();
26203
- const [name, setName] = React.useState("");
26563
+ const [name, setName] = React$1.useState("");
26204
26564
  return /* @__PURE__ */ jsxs("div", {
26205
26565
  className: "space-y-6",
26206
26566
  children: [
@@ -26376,22 +26736,22 @@ function ErrorStep({ message, code, onRetry }) {
26376
26736
  });
26377
26737
  }
26378
26738
  function SignupPage() {
26379
- const [step, setStep] = React.useState("email");
26380
- const [email, setEmail] = React.useState("");
26381
- const [error, setError] = React.useState();
26382
- const [errorCode, setErrorCode] = React.useState();
26383
- const [isLoading, setIsLoading] = React.useState(false);
26384
- const [verifyResult, setVerifyResult] = React.useState(null);
26385
- const [token, setToken] = React.useState(null);
26386
- const [resendCooldown, setResendCooldown] = React.useState(0);
26387
- React.useEffect(() => {
26739
+ const [step, setStep] = React$1.useState("email");
26740
+ const [email, setEmail] = React$1.useState("");
26741
+ const [error, setError] = React$1.useState();
26742
+ const [errorCode, setErrorCode] = React$1.useState();
26743
+ const [isLoading, setIsLoading] = React$1.useState(false);
26744
+ const [verifyResult, setVerifyResult] = React$1.useState(null);
26745
+ const [token, setToken] = React$1.useState(null);
26746
+ const [resendCooldown, setResendCooldown] = React$1.useState(0);
26747
+ React$1.useEffect(() => {
26388
26748
  const urlToken = new URLSearchParams(window.location.search).get("token");
26389
26749
  if (urlToken) {
26390
26750
  setToken(urlToken);
26391
26751
  verifyToken(urlToken);
26392
26752
  }
26393
26753
  }, []);
26394
- React.useEffect(() => {
26754
+ React$1.useEffect(() => {
26395
26755
  if (resendCooldown > 0) {
26396
26756
  const timer = setTimeout(() => setResendCooldown((c) => c - 1), 1e3);
26397
26757
  return () => clearTimeout(timer);
@@ -26556,7 +26916,7 @@ function flattenTerms(terms) {
26556
26916
  /**
26557
26917
  * Term row component (recursive for hierarchy)
26558
26918
  */
26559
- function TermRow({ term, level = 0, onEdit, onDelete }) {
26919
+ function TermRow({ term, level = 0, onEdit, onDelete, onTranslate, canTranslate }) {
26560
26920
  const { _: _t } = useLingui();
26561
26921
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
26562
26922
  className: "flex items-center gap-4 py-2 px-4 border-b hover:bg-kumo-tint/50",
@@ -26582,49 +26942,154 @@ function TermRow({ term, level = 0, onEdit, onDelete }) {
26582
26942
  }),
26583
26943
  /* @__PURE__ */ jsxs("div", {
26584
26944
  className: "flex gap-2",
26585
- children: [/* @__PURE__ */ jsx(Button, {
26586
- variant: "ghost",
26587
- size: "sm",
26588
- "aria-label": _t({
26589
- id: "N6j2JH",
26590
- message: "Edit {0}",
26591
- values: { 0: term.label }
26592
- }),
26593
- onClick: () => onEdit(term),
26594
- children: /* @__PURE__ */ jsx(Pencil, { className: "w-4 h-4" })
26595
- }), /* @__PURE__ */ jsx(Button, {
26596
- variant: "ghost",
26597
- size: "sm",
26598
- "aria-label": _t({
26599
- id: "Y2tU6I",
26600
- message: "Delete {0}",
26601
- values: { 0: term.label }
26945
+ children: [
26946
+ canTranslate && onTranslate ? /* @__PURE__ */ jsx(Button, {
26947
+ variant: "ghost",
26948
+ size: "sm",
26949
+ "aria-label": _t({
26950
+ id: "Acyeiz",
26951
+ message: "Translate {0}",
26952
+ values: { 0: term.label }
26953
+ }),
26954
+ onClick: () => onTranslate(term),
26955
+ children: _t({
26956
+ id: "pi8x/S",
26957
+ message: "Translate"
26958
+ })
26959
+ }) : null,
26960
+ /* @__PURE__ */ jsx(Button, {
26961
+ variant: "ghost",
26962
+ size: "sm",
26963
+ "aria-label": _t({
26964
+ id: "N6j2JH",
26965
+ message: "Edit {0}",
26966
+ values: { 0: term.label }
26967
+ }),
26968
+ onClick: () => onEdit(term),
26969
+ children: /* @__PURE__ */ jsx(Pencil, { className: "w-4 h-4" })
26602
26970
  }),
26603
- onClick: () => onDelete(term),
26604
- children: /* @__PURE__ */ jsx(Trash, { className: "w-4 h-4" })
26605
- })]
26971
+ /* @__PURE__ */ jsx(Button, {
26972
+ variant: "ghost",
26973
+ size: "sm",
26974
+ "aria-label": _t({
26975
+ id: "Y2tU6I",
26976
+ message: "Delete {0}",
26977
+ values: { 0: term.label }
26978
+ }),
26979
+ onClick: () => onDelete(term),
26980
+ children: /* @__PURE__ */ jsx(Trash, { className: "w-4 h-4" })
26981
+ })
26982
+ ]
26606
26983
  })
26607
26984
  ]
26608
26985
  }), term.children.map((child) => /* @__PURE__ */ jsx(TermRow, {
26609
26986
  term: child,
26610
26987
  level: level + 1,
26611
26988
  onEdit,
26612
- onDelete
26989
+ onDelete,
26990
+ onTranslate,
26991
+ canTranslate
26613
26992
  }, child.id))] });
26614
26993
  }
26615
26994
  /**
26616
- * Term form dialog
26995
+ * Dialog to pick a target locale for creating a term translation.
26617
26996
  */
26618
- function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTerms }) {
26997
+ function TranslateTermDialog({ term, taxonomyName, locales, activeLocale, isPending, error, onClose, onSubmit }) {
26619
26998
  const { _: _t2 } = useLingui();
26999
+ const otherLocales = locales.filter((l) => l !== activeLocale);
27000
+ const [selected, setSelected] = React$1.useState(otherLocales[0] ?? "");
27001
+ return /* @__PURE__ */ jsx(Dialog.Root, {
27002
+ open: true,
27003
+ onOpenChange: (isOpen) => {
27004
+ if (!isOpen) onClose();
27005
+ },
27006
+ children: /* @__PURE__ */ jsxs(Dialog, {
27007
+ className: "p-6",
27008
+ size: "sm",
27009
+ children: [
27010
+ /* @__PURE__ */ jsxs("div", {
27011
+ className: "flex items-start justify-between gap-4 mb-4",
27012
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Dialog.Title, {
27013
+ className: "text-lg font-semibold",
27014
+ children: _t2({
27015
+ id: "aCu6FM",
27016
+ message: "Translate \"{0}\"",
27017
+ values: { 0: term.label }
27018
+ })
27019
+ }), /* @__PURE__ */ jsx(Dialog.Description, {
27020
+ className: "text-sm text-kumo-subtle",
27021
+ children: _t2({
27022
+ id: "J1hptc",
27023
+ message: "Taxonomy: {taxonomyName}",
27024
+ values: { taxonomyName }
27025
+ })
27026
+ })] }), /* @__PURE__ */ jsx(Dialog.Close, { render: (props) => /* @__PURE__ */ jsx(Button, {
27027
+ ...props,
27028
+ variant: "ghost",
27029
+ shape: "square",
27030
+ "aria-label": _t2({
27031
+ id: "yz7wBu",
27032
+ message: "Close"
27033
+ }),
27034
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
27035
+ }) })]
27036
+ }),
27037
+ /* @__PURE__ */ jsxs("div", {
27038
+ className: "space-y-4 py-2",
27039
+ children: [/* @__PURE__ */ jsx(Select, {
27040
+ label: _t2({
27041
+ id: "qxt3cN",
27042
+ message: "Target locale"
27043
+ }),
27044
+ value: selected,
27045
+ onValueChange: (v) => setSelected(v ?? ""),
27046
+ items: Object.fromEntries(otherLocales.map((l) => [l, l.toUpperCase()])),
27047
+ children: otherLocales.map((l) => /* @__PURE__ */ jsx(Select.Option, {
27048
+ value: l,
27049
+ children: l.toUpperCase()
27050
+ }, l))
27051
+ }), /* @__PURE__ */ jsx(DialogError, { message: error ? error.message : null })]
27052
+ }),
27053
+ /* @__PURE__ */ jsxs("div", {
27054
+ className: "flex justify-end gap-2",
27055
+ children: [/* @__PURE__ */ jsx(Button, {
27056
+ variant: "outline",
27057
+ type: "button",
27058
+ onClick: onClose,
27059
+ children: _t2({
27060
+ id: "dEgA5A",
27061
+ message: "Cancel"
27062
+ })
27063
+ }), /* @__PURE__ */ jsx(Button, {
27064
+ type: "button",
27065
+ disabled: !selected || isPending,
27066
+ onClick: () => onSubmit(selected),
27067
+ children: isPending ? _t2({
27068
+ id: "wvgKtH",
27069
+ message: "Translating..."
27070
+ }) : _t2({
27071
+ id: "pi8x/S",
27072
+ message: "Translate"
27073
+ })
27074
+ })]
27075
+ })
27076
+ ]
27077
+ })
27078
+ });
27079
+ }
27080
+ /**
27081
+ * Term form dialog
27082
+ */
27083
+ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTerms, locale, i18n, onOpenTranslation }) {
27084
+ const { _: _t3 } = useLingui();
26620
27085
  const queryClient = useQueryClient();
26621
- const [label, setLabel] = React.useState(term?.label || "");
26622
- const [slug, setSlug] = React.useState(term?.slug || "");
26623
- const [parentId, setParentId] = React.useState(term?.parentId || "");
26624
- const [description, setDescription] = React.useState(term?.description || "");
26625
- const [autoSlug, setAutoSlug] = React.useState(!term);
26626
- const [error, setError] = React.useState(null);
26627
- React.useEffect(() => {
27086
+ const [label, setLabel] = React$1.useState(term?.label || "");
27087
+ const [slug, setSlug] = React$1.useState(term?.slug || "");
27088
+ const [parentId, setParentId] = React$1.useState(term?.parentId || "");
27089
+ const [description, setDescription] = React$1.useState(term?.description || "");
27090
+ const [autoSlug, setAutoSlug] = React$1.useState(!term);
27091
+ const [error, setError] = React$1.useState(null);
27092
+ React$1.useEffect(() => {
26628
27093
  setLabel(term?.label || "");
26629
27094
  setSlug(term?.slug || "");
26630
27095
  setParentId(term?.parentId || "");
@@ -26632,7 +27097,7 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26632
27097
  setAutoSlug(!term);
26633
27098
  setError(null);
26634
27099
  }, [term]);
26635
- React.useEffect(() => {
27100
+ React$1.useEffect(() => {
26636
27101
  if (autoSlug && label) setSlug(slugify(label));
26637
27102
  }, [label, autoSlug]);
26638
27103
  const createMutation = useMutation({
@@ -26640,7 +27105,8 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26640
27105
  slug,
26641
27106
  label,
26642
27107
  parentId: parentId || void 0,
26643
- description: description || void 0
27108
+ description: description || void 0,
27109
+ locale
26644
27110
  }),
26645
27111
  onSuccess: () => {
26646
27112
  queryClient.invalidateQueries({ queryKey: ["taxonomy-terms", taxonomyName] });
@@ -26658,7 +27124,7 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26658
27124
  label,
26659
27125
  parentId: parentId || void 0,
26660
27126
  description: description || void 0
26661
- });
27127
+ }, { locale: term.locale ?? locale });
26662
27128
  },
26663
27129
  onSuccess: () => {
26664
27130
  queryClient.invalidateQueries({ queryKey: ["taxonomy-terms", taxonomyName] });
@@ -26668,6 +27134,40 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26668
27134
  setError(err.message);
26669
27135
  }
26670
27136
  });
27137
+ const { data: translationsData } = useQuery({
27138
+ queryKey: [
27139
+ "term-translations",
27140
+ taxonomyName,
27141
+ term?.id ?? null
27142
+ ],
27143
+ queryFn: () => {
27144
+ if (!term) throw new Error("No term");
27145
+ return fetchTermTranslations(taxonomyName, term.slug, { locale: term.locale ?? locale });
27146
+ },
27147
+ enabled: !!term && !!i18n && i18n.locales.length > 1
27148
+ });
27149
+ const translateMutation = useMutation({
27150
+ mutationFn: (targetLocale) => {
27151
+ if (!term) throw new Error("No term");
27152
+ return createTermTranslation(taxonomyName, term.slug, {
27153
+ locale: targetLocale,
27154
+ label: term.label,
27155
+ slug: term.slug
27156
+ }, { locale: term.locale ?? locale });
27157
+ },
27158
+ onSuccess: (translated) => {
27159
+ queryClient.invalidateQueries({ queryKey: ["taxonomy-terms", taxonomyName] });
27160
+ queryClient.invalidateQueries({ queryKey: ["term-translations", taxonomyName] });
27161
+ onClose();
27162
+ onOpenTranslation?.({
27163
+ slug: translated.slug,
27164
+ locale: translated.locale
27165
+ });
27166
+ },
27167
+ onError: (err) => {
27168
+ setError(err.message);
27169
+ }
27170
+ });
26671
27171
  const handleSubmit = (e) => {
26672
27172
  e.preventDefault();
26673
27173
  setError(null);
@@ -26697,33 +27197,33 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26697
27197
  children: [/* @__PURE__ */ jsxs(Dialog.Title, {
26698
27198
  className: "text-lg font-semibold leading-none tracking-tight",
26699
27199
  children: [
26700
- term ? _t2({
27200
+ term ? _t3({
26701
27201
  id: "ePK91l",
26702
27202
  message: "Edit"
26703
- }) : _t2({
27203
+ }) : _t3({
26704
27204
  id: "m16xKo",
26705
27205
  message: "Add"
26706
27206
  }),
26707
27207
  " ",
26708
- taxonomyDef.labelSingular || _t2({
27208
+ taxonomyDef.labelSingular || _t3({
26709
27209
  id: "k3xOSk",
26710
27210
  message: "Term"
26711
27211
  })
26712
27212
  ]
26713
27213
  }), /* @__PURE__ */ jsx(Dialog.Description, {
26714
27214
  className: "text-sm text-kumo-subtle",
26715
- children: term ? _t2({
27215
+ children: term ? _t3({
26716
27216
  id: "EWH8E/",
26717
27217
  message: "Update the {0} details",
26718
27218
  values: { 0: taxonomyDef.labelSingular?.toLowerCase() || "term" }
26719
- }) : _t2({
27219
+ }) : _t3({
26720
27220
  id: "bwUFyt",
26721
27221
  message: "Create a new {0}",
26722
27222
  values: { 0: taxonomyDef.labelSingular?.toLowerCase() || "term" }
26723
27223
  })
26724
27224
  })]
26725
27225
  }), /* @__PURE__ */ jsx(Dialog.Close, {
26726
- "aria-label": _t2({
27226
+ "aria-label": _t3({
26727
27227
  id: "yz7wBu",
26728
27228
  message: "Close"
26729
27229
  }),
@@ -26731,14 +27231,14 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26731
27231
  ...props,
26732
27232
  variant: "ghost",
26733
27233
  shape: "square",
26734
- "aria-label": _t2({
27234
+ "aria-label": _t3({
26735
27235
  id: "yz7wBu",
26736
27236
  message: "Close"
26737
27237
  }),
26738
27238
  className: "absolute end-4 top-4",
26739
27239
  children: [/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", {
26740
27240
  className: "sr-only",
26741
- children: _t2({
27241
+ children: _t3({
26742
27242
  id: "yz7wBu",
26743
27243
  message: "Close"
26744
27244
  })
@@ -26750,7 +27250,7 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26750
27250
  className: "space-y-4 py-4",
26751
27251
  children: [
26752
27252
  /* @__PURE__ */ jsx(Input, {
26753
- label: _t2({
27253
+ label: _t3({
26754
27254
  id: "6YtxFj",
26755
27255
  message: "Name"
26756
27256
  }),
@@ -26760,7 +27260,7 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26760
27260
  required: true
26761
27261
  }),
26762
27262
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
26763
- label: _t2({
27263
+ label: _t3({
26764
27264
  id: "L85WcV",
26765
27265
  message: "Slug"
26766
27266
  }),
@@ -26773,20 +27273,20 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26773
27273
  required: true
26774
27274
  }), /* @__PURE__ */ jsx("p", {
26775
27275
  className: "text-sm text-kumo-subtle mt-1",
26776
- children: _t2({
27276
+ children: _t3({
26777
27277
  id: "WA0tbJ",
26778
27278
  message: "Auto-generated from name (you can edit)"
26779
27279
  })
26780
27280
  })] }),
26781
27281
  taxonomyDef.hierarchical && /* @__PURE__ */ jsxs(Select, {
26782
- label: _t2({
27282
+ label: _t3({
26783
27283
  id: "sujToP",
26784
27284
  message: "Parent"
26785
27285
  }),
26786
27286
  value: parentId,
26787
27287
  onValueChange: (v) => setParentId(v ?? ""),
26788
27288
  items: {
26789
- "": _t2({
27289
+ "": _t3({
26790
27290
  id: "0KpSnU",
26791
27291
  message: "None (top level)"
26792
27292
  }),
@@ -26794,7 +27294,7 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26794
27294
  },
26795
27295
  children: [/* @__PURE__ */ jsx(Select.Option, {
26796
27296
  value: "",
26797
- children: _t2({
27297
+ children: _t3({
26798
27298
  id: "0KpSnU",
26799
27299
  message: "None (top level)"
26800
27300
  })
@@ -26804,19 +27304,37 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26804
27304
  }, parentTerm.id))]
26805
27305
  }),
26806
27306
  /* @__PURE__ */ jsx(InputArea, {
26807
- label: _t2({
27307
+ label: _t3({
26808
27308
  id: "Fxf4jq",
26809
27309
  message: "Description (optional)"
26810
27310
  }),
26811
27311
  value: description,
26812
27312
  onChange: (e) => setDescription(e.target.value),
26813
- placeholder: _t2({
27313
+ placeholder: _t3({
26814
27314
  id: "pbzA+s",
26815
27315
  message: "Optional description"
26816
27316
  }),
26817
27317
  rows: 3
26818
27318
  }),
26819
- /* @__PURE__ */ jsx(DialogError, { message: error || getMutationError(createMutation.error) || getMutationError(updateMutation.error) })
27319
+ /* @__PURE__ */ jsx(DialogError, { message: error || getMutationError(createMutation.error) || getMutationError(updateMutation.error) || getMutationError(translateMutation.error) }),
27320
+ term && i18n && i18n.locales.length > 1 ? /* @__PURE__ */ jsx("div", {
27321
+ className: "pt-4 border-t",
27322
+ children: /* @__PURE__ */ jsx(TranslationsPanel, {
27323
+ locales: i18n.locales,
27324
+ defaultLocale: i18n.defaultLocale,
27325
+ currentLocale: term.locale,
27326
+ translations: translationsData?.translations ?? [],
27327
+ onOpen: (tr) => {
27328
+ onClose();
27329
+ onOpenTranslation?.({
27330
+ slug: term.slug,
27331
+ locale: tr.locale
27332
+ });
27333
+ },
27334
+ onCreate: (target) => translateMutation.mutate(target),
27335
+ pendingLocale: translateMutation.isPending ? translateMutation.variables ?? null : null
27336
+ })
27337
+ }) : null
26820
27338
  ]
26821
27339
  }),
26822
27340
  /* @__PURE__ */ jsxs("div", {
@@ -26825,20 +27343,20 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26825
27343
  type: "button",
26826
27344
  variant: "outline",
26827
27345
  onClick: onClose,
26828
- children: _t2({
27346
+ children: _t3({
26829
27347
  id: "dEgA5A",
26830
27348
  message: "Cancel"
26831
27349
  })
26832
27350
  }), /* @__PURE__ */ jsx(Button, {
26833
27351
  type: "submit",
26834
27352
  disabled: createMutation.isPending || updateMutation.isPending,
26835
- children: createMutation.isPending || updateMutation.isPending ? _t2({
27353
+ children: createMutation.isPending || updateMutation.isPending ? _t3({
26836
27354
  id: "XvjC4F",
26837
27355
  message: "Saving..."
26838
- }) : term ? _t2({
27356
+ }) : term ? _t3({
26839
27357
  id: "EkH9pt",
26840
27358
  message: "Update"
26841
- }) : _t2({
27359
+ }) : _t3({
26842
27360
  id: "hYgDIe",
26843
27361
  message: "Create"
26844
27362
  })
@@ -26853,14 +27371,14 @@ function TermFormDialog({ open, onClose, taxonomyName, taxonomyDef, term, allTer
26853
27371
  * Create Taxonomy dialog
26854
27372
  */
26855
27373
  function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26856
- const { _: _t3 } = useLingui();
27374
+ const { _: _t4 } = useLingui();
26857
27375
  const queryClient = useQueryClient();
26858
- const [name, setName] = React.useState("");
26859
- const [label, setLabel] = React.useState("");
26860
- const [hierarchical, setHierarchical] = React.useState(false);
26861
- const [selectedCollections, setSelectedCollections] = React.useState([]);
26862
- const [autoName, setAutoName] = React.useState(true);
26863
- const [error, setError] = React.useState(null);
27376
+ const [name, setName] = React$1.useState("");
27377
+ const [label, setLabel] = React$1.useState("");
27378
+ const [hierarchical, setHierarchical] = React$1.useState(false);
27379
+ const [selectedCollections, setSelectedCollections] = React$1.useState([]);
27380
+ const [autoName, setAutoName] = React$1.useState(true);
27381
+ const [error, setError] = React$1.useState(null);
26864
27382
  const { data: manifest } = useQuery({
26865
27383
  queryKey: ["manifest"],
26866
27384
  queryFn: fetchManifest
@@ -26869,7 +27387,7 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26869
27387
  slug,
26870
27388
  label: config.label
26871
27389
  })) : [];
26872
- React.useEffect(() => {
27390
+ React$1.useEffect(() => {
26873
27391
  if (autoName && label) setName(label.toLowerCase().replace(NON_ALPHANUMERIC_PATTERN, "_").replace(LEADING_TRAILING_UNDERSCORE_PATTERN, ""));
26874
27392
  }, [label, autoName]);
26875
27393
  const createMutation = useMutation({
@@ -26894,14 +27412,14 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26894
27412
  e.preventDefault();
26895
27413
  setError(null);
26896
27414
  if (!name || !label) {
26897
- setError(_t3({
27415
+ setError(_t4({
26898
27416
  id: "6RLorB",
26899
27417
  message: "Name and label are required"
26900
27418
  }));
26901
27419
  return;
26902
27420
  }
26903
27421
  if (!TAXONOMY_NAME_PATTERN.test(name)) {
26904
- setError(_t3({
27422
+ setError(_t4({
26905
27423
  id: "rpb1yP",
26906
27424
  message: "Name must start with a letter and contain only lowercase letters, numbers, and underscores"
26907
27425
  }));
@@ -26937,19 +27455,19 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26937
27455
  className: "flex flex-col space-y-1.5",
26938
27456
  children: [/* @__PURE__ */ jsx(Dialog.Title, {
26939
27457
  className: "text-lg font-semibold leading-none tracking-tight",
26940
- children: _t3({
27458
+ children: _t4({
26941
27459
  id: "xOmArx",
26942
27460
  message: "Create Taxonomy"
26943
27461
  })
26944
27462
  }), /* @__PURE__ */ jsx(Dialog.Description, {
26945
27463
  className: "text-sm text-kumo-subtle",
26946
- children: _t3({
27464
+ children: _t4({
26947
27465
  id: "piag2l",
26948
27466
  message: "Define a new taxonomy for classifying content"
26949
27467
  })
26950
27468
  })]
26951
27469
  }), /* @__PURE__ */ jsx(Dialog.Close, {
26952
- "aria-label": _t3({
27470
+ "aria-label": _t4({
26953
27471
  id: "yz7wBu",
26954
27472
  message: "Close"
26955
27473
  }),
@@ -26957,14 +27475,14 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26957
27475
  ...props,
26958
27476
  variant: "ghost",
26959
27477
  shape: "square",
26960
- "aria-label": _t3({
27478
+ "aria-label": _t4({
26961
27479
  id: "yz7wBu",
26962
27480
  message: "Close"
26963
27481
  }),
26964
27482
  className: "absolute end-4 top-4",
26965
27483
  children: [/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }), /* @__PURE__ */ jsx("span", {
26966
27484
  className: "sr-only",
26967
- children: _t3({
27485
+ children: _t4({
26968
27486
  id: "yz7wBu",
26969
27487
  message: "Close"
26970
27488
  })
@@ -26976,7 +27494,7 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26976
27494
  className: "space-y-4 py-4",
26977
27495
  children: [
26978
27496
  /* @__PURE__ */ jsx(Input, {
26979
- label: _t3({
27497
+ label: _t4({
26980
27498
  id: "87a/t/",
26981
27499
  message: "Label"
26982
27500
  }),
@@ -26986,7 +27504,7 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26986
27504
  required: true
26987
27505
  }),
26988
27506
  /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Input, {
26989
- label: _t3({
27507
+ label: _t4({
26990
27508
  id: "6YtxFj",
26991
27509
  message: "Name"
26992
27510
  }),
@@ -26998,19 +27516,19 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
26998
27516
  placeholder: "genre",
26999
27517
  required: true,
27000
27518
  pattern: "[a-z][a-z0-9_]*",
27001
- title: _t3({
27519
+ title: _t4({
27002
27520
  id: "vVZq2l",
27003
27521
  message: "Lowercase letters, numbers, and underscores only, starting with a letter"
27004
27522
  })
27005
27523
  }), /* @__PURE__ */ jsx("p", {
27006
27524
  className: "text-xs text-kumo-subtle mt-1",
27007
- children: _t3({
27525
+ children: _t4({
27008
27526
  id: "eEF9iG",
27009
27527
  message: "Used as the identifier. Lowercase letters, numbers, and underscores only."
27010
27528
  })
27011
27529
  })] }),
27012
27530
  /* @__PURE__ */ jsx(Checkbox, {
27013
- label: _t3({
27531
+ label: _t4({
27014
27532
  id: "BUeYF7",
27015
27533
  message: "Hierarchical (like categories, with parent/child relationships)"
27016
27534
  }),
@@ -27020,14 +27538,14 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
27020
27538
  collectionEntries.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
27021
27539
  /* @__PURE__ */ jsx("label", {
27022
27540
  className: "text-sm font-medium",
27023
- children: _t3({
27541
+ children: _t4({
27024
27542
  id: "DoJzLz",
27025
27543
  message: "Collections"
27026
27544
  })
27027
27545
  }),
27028
27546
  /* @__PURE__ */ jsx("p", {
27029
27547
  className: "text-xs text-kumo-subtle mb-2",
27030
- children: _t3({
27548
+ children: _t4({
27031
27549
  id: "STKTcL",
27032
27550
  message: "Which content types can use this taxonomy"
27033
27551
  })
@@ -27060,17 +27578,17 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
27060
27578
  resetForm();
27061
27579
  onClose();
27062
27580
  },
27063
- children: _t3({
27581
+ children: _t4({
27064
27582
  id: "dEgA5A",
27065
27583
  message: "Cancel"
27066
27584
  })
27067
27585
  }), /* @__PURE__ */ jsx(Button, {
27068
27586
  type: "submit",
27069
27587
  disabled: createMutation.isPending,
27070
- children: createMutation.isPending ? _t3({
27588
+ children: createMutation.isPending ? _t4({
27071
27589
  id: "HM56Bx",
27072
27590
  message: "Creating..."
27073
- }) : _t3({
27591
+ }) : _t4({
27074
27592
  id: "xOmArx",
27075
27593
  message: "Create Taxonomy"
27076
27594
  })
@@ -27085,32 +27603,72 @@ function CreateTaxonomyDialog({ open, onClose, onCreated }) {
27085
27603
  * Main TaxonomyManager component
27086
27604
  */
27087
27605
  function TaxonomyManager({ taxonomyName }) {
27088
- const { _: _t4 } = useLingui();
27606
+ const { _: _t5 } = useLingui();
27089
27607
  const queryClient = useQueryClient();
27090
27608
  const toastManager = Toast.useToastManager();
27091
- const [formOpen, setFormOpen] = React.useState(false);
27092
- const [editingTerm, setEditingTerm] = React.useState();
27093
- const [deleteTarget, setDeleteTarget] = React.useState(null);
27094
- const [createTaxonomyOpen, setCreateTaxonomyOpen] = React.useState(false);
27609
+ const [formOpen, setFormOpen] = React$1.useState(false);
27610
+ const [editingTerm, setEditingTerm] = React$1.useState();
27611
+ const [deleteTarget, setDeleteTarget] = React$1.useState(null);
27612
+ const [createTaxonomyOpen, setCreateTaxonomyOpen] = React$1.useState(false);
27613
+ const [translateTarget, setTranslateTarget] = React$1.useState(null);
27614
+ const { data: manifest } = useQuery({
27615
+ queryKey: ["manifest"],
27616
+ queryFn: fetchManifest
27617
+ });
27618
+ const i18n = useI18nConfig(manifest);
27619
+ const [activeLocale, setActiveLocale] = React$1.useState(void 0);
27620
+ React$1.useEffect(() => {
27621
+ if (i18n && !activeLocale) setActiveLocale(i18n.defaultLocale);
27622
+ }, [i18n, activeLocale]);
27095
27623
  const { data: taxonomyDef, isLoading: defLoading } = useQuery({
27096
27624
  queryKey: ["taxonomy-def", taxonomyName],
27097
27625
  queryFn: () => fetchTaxonomyDef(taxonomyName)
27098
27626
  });
27099
27627
  const { data: terms = [], isLoading: termsLoading } = useQuery({
27100
- queryKey: ["taxonomy-terms", taxonomyName],
27101
- queryFn: () => fetchTerms(taxonomyName)
27628
+ queryKey: [
27629
+ "taxonomy-terms",
27630
+ taxonomyName,
27631
+ activeLocale
27632
+ ],
27633
+ queryFn: () => fetchTerms(taxonomyName, { locale: activeLocale })
27102
27634
  });
27103
27635
  const deleteMutation = useMutation({
27104
- mutationFn: (term) => deleteTerm(taxonomyName, term.slug),
27636
+ mutationFn: (term) => deleteTerm(taxonomyName, term.slug, { locale: activeLocale }),
27105
27637
  onSuccess: () => {
27106
27638
  queryClient.invalidateQueries({ queryKey: ["taxonomy-terms", taxonomyName] });
27107
27639
  setDeleteTarget(null);
27108
- toastManager.add({ title: _t4({
27640
+ toastManager.add({ title: _t5({
27109
27641
  id: "SXhLSY",
27110
27642
  message: "Term deleted"
27111
27643
  }) });
27112
27644
  }
27113
27645
  });
27646
+ const translateMutation = useMutation({
27647
+ mutationFn: ({ term, locale }) => createTermTranslation(taxonomyName, term.slug, {
27648
+ locale,
27649
+ label: term.label,
27650
+ slug: term.slug
27651
+ }, { locale: activeLocale }),
27652
+ onSuccess: (term) => {
27653
+ queryClient.invalidateQueries({ queryKey: ["taxonomy-terms", taxonomyName] });
27654
+ setTranslateTarget(null);
27655
+ setActiveLocale(term.locale);
27656
+ toastManager.add({
27657
+ title: _t5({
27658
+ id: "pBdGCu",
27659
+ message: "Translation created"
27660
+ }),
27661
+ description: _t5({
27662
+ id: "X974Zi",
27663
+ message: "Term \"{0}\" created in {1}.",
27664
+ values: {
27665
+ 0: term.label,
27666
+ 1: term.locale.toUpperCase()
27667
+ }
27668
+ })
27669
+ });
27670
+ }
27671
+ });
27114
27672
  const handleEdit = (term) => {
27115
27673
  setEditingTerm(term);
27116
27674
  setFormOpen(true);
@@ -27122,12 +27680,12 @@ function TaxonomyManager({ taxonomyName }) {
27122
27680
  setFormOpen(false);
27123
27681
  setEditingTerm(void 0);
27124
27682
  };
27125
- if (defLoading) return /* @__PURE__ */ jsx("div", { children: _t4({
27683
+ if (defLoading) return /* @__PURE__ */ jsx("div", { children: _t5({
27126
27684
  id: "Z3FXyt",
27127
27685
  message: "Loading..."
27128
27686
  }) });
27129
27687
  if (!taxonomyDef) return /* @__PURE__ */ jsxs("div", { children: [
27130
- _t4({
27688
+ _t5({
27131
27689
  id: "8EXwpa",
27132
27690
  message: "Taxonomy not found:"
27133
27691
  }),
@@ -27139,13 +27697,13 @@ function TaxonomyManager({ taxonomyName }) {
27139
27697
  className: "space-y-6",
27140
27698
  children: [
27141
27699
  /* @__PURE__ */ jsxs("div", {
27142
- className: "flex items-center justify-between",
27700
+ className: "flex items-center justify-between gap-4 flex-wrap",
27143
27701
  children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("h1", {
27144
27702
  className: "text-3xl font-bold",
27145
27703
  children: taxonomyDef.label
27146
27704
  }), /* @__PURE__ */ jsx("p", {
27147
27705
  className: "text-kumo-subtle mt-1",
27148
- children: _t4({
27706
+ children: _t5({
27149
27707
  id: "H1WnmU",
27150
27708
  message: "Manage {0} for {1}",
27151
27709
  values: {
@@ -27154,30 +27712,39 @@ function TaxonomyManager({ taxonomyName }) {
27154
27712
  }
27155
27713
  })
27156
27714
  })] }), /* @__PURE__ */ jsxs("div", {
27157
- className: "flex gap-2",
27158
- children: [/* @__PURE__ */ jsx(Button, {
27159
- variant: "outline",
27160
- icon: /* @__PURE__ */ jsx(Plus, {}),
27161
- onClick: () => setCreateTaxonomyOpen(true),
27162
- children: _t4({
27163
- id: "rrNQ2v",
27164
- message: "New Taxonomy"
27165
- })
27166
- }), /* @__PURE__ */ jsxs(Button, {
27167
- icon: /* @__PURE__ */ jsx(Plus, {}),
27168
- onClick: () => setFormOpen(true),
27169
- children: [
27170
- _t4({
27171
- id: "m16xKo",
27172
- message: "Add"
27173
- }),
27174
- " ",
27175
- taxonomyDef.labelSingular || _t4({
27176
- id: "k3xOSk",
27177
- message: "Term"
27715
+ className: "flex gap-2 items-center",
27716
+ children: [
27717
+ i18n && activeLocale ? /* @__PURE__ */ jsx(LocaleSwitcher, {
27718
+ locales: i18n.locales,
27719
+ defaultLocale: i18n.defaultLocale,
27720
+ value: activeLocale,
27721
+ onChange: setActiveLocale
27722
+ }) : null,
27723
+ /* @__PURE__ */ jsx(Button, {
27724
+ variant: "outline",
27725
+ icon: /* @__PURE__ */ jsx(Plus, {}),
27726
+ onClick: () => setCreateTaxonomyOpen(true),
27727
+ children: _t5({
27728
+ id: "rrNQ2v",
27729
+ message: "New Taxonomy"
27178
27730
  })
27179
- ]
27180
- })]
27731
+ }),
27732
+ /* @__PURE__ */ jsxs(Button, {
27733
+ icon: /* @__PURE__ */ jsx(Plus, {}),
27734
+ onClick: () => setFormOpen(true),
27735
+ children: [
27736
+ _t5({
27737
+ id: "m16xKo",
27738
+ message: "Add"
27739
+ }),
27740
+ " ",
27741
+ taxonomyDef.labelSingular || _t5({
27742
+ id: "k3xOSk",
27743
+ message: "Term"
27744
+ })
27745
+ ]
27746
+ })
27747
+ ]
27181
27748
  })]
27182
27749
  }),
27183
27750
  /* @__PURE__ */ jsxs("div", {
@@ -27187,21 +27754,21 @@ function TaxonomyManager({ taxonomyName }) {
27187
27754
  children: [
27188
27755
  /* @__PURE__ */ jsx("div", {
27189
27756
  className: "flex-1",
27190
- children: _t4({
27757
+ children: _t5({
27191
27758
  id: "6YtxFj",
27192
27759
  message: "Name"
27193
27760
  })
27194
27761
  }),
27195
27762
  /* @__PURE__ */ jsx("div", {
27196
27763
  className: "w-16 text-center",
27197
- children: _t4({
27764
+ children: _t5({
27198
27765
  id: "wBMjJ2",
27199
27766
  message: "Count"
27200
27767
  })
27201
27768
  }),
27202
27769
  /* @__PURE__ */ jsx("div", {
27203
27770
  className: "w-24 text-center",
27204
- children: _t4({
27771
+ children: _t5({
27205
27772
  id: "7L01XJ",
27206
27773
  message: "Actions"
27207
27774
  })
@@ -27209,13 +27776,13 @@ function TaxonomyManager({ taxonomyName }) {
27209
27776
  ]
27210
27777
  }), termsLoading ? /* @__PURE__ */ jsx("div", {
27211
27778
  className: "p-8 text-center text-kumo-subtle",
27212
- children: _t4({
27779
+ children: _t5({
27213
27780
  id: "x/4m9x",
27214
27781
  message: "Loading terms..."
27215
27782
  })
27216
27783
  }) : terms.length === 0 ? /* @__PURE__ */ jsx("div", {
27217
27784
  className: "p-8 text-center text-kumo-subtle",
27218
- children: _t4({
27785
+ children: _t5({
27219
27786
  id: "R3O8Io",
27220
27787
  message: "No {0} yet. Create one to get started.",
27221
27788
  values: { 0: taxonomyDef.label.toLowerCase() }
@@ -27223,7 +27790,9 @@ function TaxonomyManager({ taxonomyName }) {
27223
27790
  }) : /* @__PURE__ */ jsx("div", { children: terms.map((term) => /* @__PURE__ */ jsx(TermRow, {
27224
27791
  term,
27225
27792
  onEdit: handleEdit,
27226
- onDelete: handleDelete
27793
+ onDelete: handleDelete,
27794
+ onTranslate: setTranslateTarget,
27795
+ canTranslate: !!i18n && !!activeLocale && i18n.locales.length > 1
27227
27796
  }, term.id)) })]
27228
27797
  }),
27229
27798
  /* @__PURE__ */ jsx(TermFormDialog, {
@@ -27232,29 +27801,48 @@ function TaxonomyManager({ taxonomyName }) {
27232
27801
  taxonomyName,
27233
27802
  taxonomyDef,
27234
27803
  term: editingTerm,
27235
- allTerms: flatTerms
27804
+ allTerms: flatTerms,
27805
+ locale: activeLocale,
27806
+ i18n,
27807
+ onOpenTranslation: (tr) => setActiveLocale(tr.locale)
27236
27808
  }),
27809
+ i18n && translateTarget ? /* @__PURE__ */ jsx(TranslateTermDialog, {
27810
+ term: translateTarget,
27811
+ taxonomyName,
27812
+ locales: i18n.locales,
27813
+ activeLocale,
27814
+ isPending: translateMutation.isPending,
27815
+ error: translateMutation.error,
27816
+ onClose: () => {
27817
+ setTranslateTarget(null);
27818
+ translateMutation.reset();
27819
+ },
27820
+ onSubmit: (locale) => translateMutation.mutate({
27821
+ term: translateTarget,
27822
+ locale
27823
+ })
27824
+ }) : null,
27237
27825
  /* @__PURE__ */ jsx(ConfirmDialog, {
27238
27826
  open: !!deleteTarget,
27239
27827
  onClose: () => {
27240
27828
  setDeleteTarget(null);
27241
27829
  deleteMutation.reset();
27242
27830
  },
27243
- title: _t4({
27831
+ title: _t5({
27244
27832
  id: "Tf88pC",
27245
27833
  message: "Delete {0}?",
27246
27834
  values: { 0: taxonomyDef.labelSingular || "Term" }
27247
27835
  }),
27248
- description: /* @__PURE__ */ jsx(Fragment, { children: _t4({
27836
+ description: /* @__PURE__ */ jsx(Fragment, { children: _t5({
27249
27837
  id: "JKi9lM",
27250
27838
  message: "This will permanently delete \"{0}\" and remove it from all content.",
27251
27839
  values: { 0: deleteTarget?.label }
27252
27840
  }) }),
27253
- confirmLabel: _t4({
27841
+ confirmLabel: _t5({
27254
27842
  id: "cnGeoo",
27255
27843
  message: "Delete"
27256
27844
  }),
27257
- pendingLabel: _t4({
27845
+ pendingLabel: _t5({
27258
27846
  id: "EF2EU9",
27259
27847
  message: "Deleting..."
27260
27848
  }),
@@ -27267,7 +27855,7 @@ function TaxonomyManager({ taxonomyName }) {
27267
27855
  onClose: () => setCreateTaxonomyOpen(false),
27268
27856
  onCreated: () => {
27269
27857
  setCreateTaxonomyOpen(false);
27270
- toastManager.add({ title: _t4({
27858
+ toastManager.add({ title: _t5({
27271
27859
  id: "pmEpis",
27272
27860
  message: "Taxonomy created"
27273
27861
  }) });
@@ -27300,10 +27888,10 @@ function isSortOption(value) {
27300
27888
  }
27301
27889
  function ThemeMarketplaceBrowse() {
27302
27890
  const { _: _t } = useLingui();
27303
- const [searchQuery, setSearchQuery] = React.useState("");
27304
- const [sort, setSort] = React.useState("updated");
27305
- const [debouncedQuery, setDebouncedQuery] = React.useState("");
27306
- React.useEffect(() => {
27891
+ const [searchQuery, setSearchQuery] = React$1.useState("");
27892
+ const [sort, setSort] = React$1.useState("updated");
27893
+ const [debouncedQuery, setDebouncedQuery] = React$1.useState("");
27894
+ React$1.useEffect(() => {
27307
27895
  const timer = setTimeout(setDebouncedQuery, 300, searchQuery);
27308
27896
  return () => clearTimeout(timer);
27309
27897
  }, [searchQuery]);
@@ -27569,7 +28157,7 @@ function isSafeUrl(url) {
27569
28157
  }
27570
28158
  function ThemeMarketplaceDetail({ themeId }) {
27571
28159
  const { _: _t } = useLingui();
27572
- const [lightboxIndex, setLightboxIndex] = React.useState(null);
28160
+ const [lightboxIndex, setLightboxIndex] = React$1.useState(null);
27573
28161
  const { data: theme, isLoading, error } = useQuery({
27574
28162
  queryKey: [
27575
28163
  "themes",
@@ -27779,7 +28367,7 @@ function ThemeMarketplaceDetail({ themeId }) {
27779
28367
  }
27780
28368
  function Lightbox({ urls, index, onClose, onPrev, onNext }) {
27781
28369
  const { _: _t2 } = useLingui();
27782
- React.useEffect(() => {
28370
+ React$1.useEffect(() => {
27783
28371
  function onKeyDown(e) {
27784
28372
  if (e.key === "Escape") onClose();
27785
28373
  if (e.key === "ArrowLeft") onPrev();
@@ -27901,20 +28489,20 @@ function Widgets() {
27901
28489
  const { _: _t } = useLingui();
27902
28490
  const queryClient = useQueryClient();
27903
28491
  const toastManager = Toast.useToastManager();
27904
- const [isCreateAreaOpen, setIsCreateAreaOpen] = React.useState(false);
27905
- const [createAreaError, setCreateAreaError] = React.useState(null);
27906
- const [activeId, setActiveId] = React.useState(null);
27907
- const [activeDragData, setActiveDragData] = React.useState(null);
27908
- const [expandedWidgets, setExpandedWidgets] = React.useState(/* @__PURE__ */ new Set());
27909
- const [blockSidebarPanel, setBlockSidebarPanel] = React.useState(null);
27910
- const draggingFromPaletteRef = React.useRef(false);
27911
- const handleBlockSidebarOpen = React.useCallback((panel) => {
28492
+ const [isCreateAreaOpen, setIsCreateAreaOpen] = React$1.useState(false);
28493
+ const [createAreaError, setCreateAreaError] = React$1.useState(null);
28494
+ const [activeId, setActiveId] = React$1.useState(null);
28495
+ const [activeDragData, setActiveDragData] = React$1.useState(null);
28496
+ const [expandedWidgets, setExpandedWidgets] = React$1.useState(/* @__PURE__ */ new Set());
28497
+ const [blockSidebarPanel, setBlockSidebarPanel] = React$1.useState(null);
28498
+ const draggingFromPaletteRef = React$1.useRef(false);
28499
+ const handleBlockSidebarOpen = React$1.useCallback((panel) => {
27912
28500
  setBlockSidebarPanel((prev) => {
27913
28501
  prev?.onClose();
27914
28502
  return panel;
27915
28503
  });
27916
28504
  }, []);
27917
- const handleBlockSidebarClose = React.useCallback(() => {
28505
+ const handleBlockSidebarClose = React$1.useCallback(() => {
27918
28506
  setBlockSidebarPanel((prev) => {
27919
28507
  prev?.onClose();
27920
28508
  return null;
@@ -27932,7 +28520,7 @@ function Widgets() {
27932
28520
  queryKey: ["manifest"],
27933
28521
  queryFn: fetchManifest
27934
28522
  });
27935
- const pluginBlocks = React.useMemo(() => manifest ? getPluginBlocks(manifest) : [], [manifest]);
28523
+ const pluginBlocks = React$1.useMemo(() => manifest ? getPluginBlocks(manifest) : [], [manifest]);
27936
28524
  const createAreaMutation = useMutation({
27937
28525
  mutationFn: createWidgetArea,
27938
28526
  onSuccess: () => {
@@ -27972,7 +28560,7 @@ function Widgets() {
27972
28560
  });
27973
28561
  };
27974
28562
  const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
27975
- const collisionDetection = React.useCallback((args) => {
28563
+ const collisionDetection = React$1.useCallback((args) => {
27976
28564
  const dragData = args.active.data.current;
27977
28565
  if (dragData && isPaletteItem(dragData)) {
27978
28566
  const areaContainers = args.droppableContainers.filter((c) => String(c.id).startsWith("area:"));
@@ -28269,7 +28857,7 @@ function DraggablePaletteItem({ id, label, description, widgetInput }) {
28269
28857
  function WidgetAreaPanel({ area, expandedWidgets, onToggleWidget, isDraggingPalette, components, pluginBlocks, onBlockSidebarOpen, onBlockSidebarClose }) {
28270
28858
  const queryClient = useQueryClient();
28271
28859
  const toastManager = Toast.useToastManager();
28272
- const [deleteAreaName, setDeleteAreaName] = React.useState(null);
28860
+ const [deleteAreaName, setDeleteAreaName] = React$1.useState(null);
28273
28861
  const { setNodeRef: setDropRef, isOver } = useDroppable({ id: `area:${area.name}` });
28274
28862
  const deleteAreaMutation = useMutation({
28275
28863
  mutationFn: deleteWidgetArea,
@@ -28444,14 +29032,14 @@ function WidgetItem({ widget, areaName, isExpanded, onToggle, components, plugin
28444
29032
  }
28445
29033
  /** Inline editor form for a widget, rendered when the widget is expanded */
28446
29034
  function WidgetEditor({ widget, components, pluginBlocks, onSave, isSaving, onBlockSidebarOpen, onBlockSidebarClose }) {
28447
- const [title, setTitle] = React.useState(widget.title ?? "");
28448
- const [content, setContent] = React.useState(Array.isArray(widget.content) ? widget.content : []);
28449
- const [menuName, setMenuName] = React.useState(widget.menuName ?? "");
28450
- const [componentId, setComponentId] = React.useState(widget.componentId ?? "");
28451
- const [componentProps, setComponentProps] = React.useState(widget.componentProps ?? {});
29035
+ const [title, setTitle] = React$1.useState(widget.title ?? "");
29036
+ const [content, setContent] = React$1.useState(Array.isArray(widget.content) ? widget.content : []);
29037
+ const [menuName, setMenuName] = React$1.useState(widget.menuName ?? "");
29038
+ const [componentId, setComponentId] = React$1.useState(widget.componentId ?? "");
29039
+ const [componentProps, setComponentProps] = React$1.useState(widget.componentProps ?? {});
28452
29040
  const { data: menus = [] } = useQuery({
28453
29041
  queryKey: ["menus"],
28454
- queryFn: fetchMenus,
29042
+ queryFn: () => fetchMenus(),
28455
29043
  enabled: widget.type === "menu"
28456
29044
  });
28457
29045
  const selectedComponent = components.find((c) => c.id === componentId);
@@ -28588,32 +29176,32 @@ function ComponentPropField({ def, value, onChange }) {
28588
29176
  const TRAILING_SLASH_REGEX = /\/$/;
28589
29177
  const WHITESPACE_REGEX = /\s/g;
28590
29178
  function WordPressImport() {
28591
- const [step, setStep] = React.useState("choose");
28592
- const [urlInput, setUrlInput] = React.useState("");
28593
- const [probeResult, setProbeResult] = React.useState(null);
28594
- const [importSource, setImportSource] = React.useState(null);
28595
- const [_file, setFile] = React.useState(null);
28596
- const [analysis, setAnalysis] = React.useState(null);
28597
- const [pluginUsername, setPluginUsername] = React.useState("");
28598
- const [pluginPassword, setPluginPassword] = React.useState("");
28599
- const [selections, setSelections] = React.useState({});
28600
- const [prepareResult, setPrepareResult] = React.useState(null);
28601
- const [result, setResult] = React.useState(null);
28602
- const [expandedTypes, setExpandedTypes] = React.useState(/* @__PURE__ */ new Set());
28603
- const [prepareError, setPrepareError] = React.useState(null);
28604
- const [importError, setImportError] = React.useState(null);
28605
- const [mediaResult, setMediaResult] = React.useState(null);
28606
- const [rewriteResult, setRewriteResult] = React.useState(null);
28607
- const [mediaError, setMediaError] = React.useState(null);
28608
- const [skipMedia, setSkipMedia] = React.useState(false);
28609
- const [mediaProgress, setMediaProgress] = React.useState(null);
28610
- const [importMenus, setImportMenus] = React.useState(true);
28611
- const [importSiteTitle, setImportSiteTitle] = React.useState(true);
28612
- const [importLogo, setImportLogo] = React.useState(true);
28613
- const [importSeo, setImportSeo] = React.useState(false);
28614
- const [authorMappings, setAuthorMappings] = React.useState([]);
28615
- const [emdashUsers, setEmDashUsers] = React.useState([]);
28616
- const initializeAuthorMappings = React.useCallback((importAnalysis, users) => {
29179
+ const [step, setStep] = React$1.useState("choose");
29180
+ const [urlInput, setUrlInput] = React$1.useState("");
29181
+ const [probeResult, setProbeResult] = React$1.useState(null);
29182
+ const [importSource, setImportSource] = React$1.useState(null);
29183
+ const [_file, setFile] = React$1.useState(null);
29184
+ const [analysis, setAnalysis] = React$1.useState(null);
29185
+ const [pluginUsername, setPluginUsername] = React$1.useState("");
29186
+ const [pluginPassword, setPluginPassword] = React$1.useState("");
29187
+ const [selections, setSelections] = React$1.useState({});
29188
+ const [prepareResult, setPrepareResult] = React$1.useState(null);
29189
+ const [result, setResult] = React$1.useState(null);
29190
+ const [expandedTypes, setExpandedTypes] = React$1.useState(/* @__PURE__ */ new Set());
29191
+ const [prepareError, setPrepareError] = React$1.useState(null);
29192
+ const [importError, setImportError] = React$1.useState(null);
29193
+ const [mediaResult, setMediaResult] = React$1.useState(null);
29194
+ const [rewriteResult, setRewriteResult] = React$1.useState(null);
29195
+ const [mediaError, setMediaError] = React$1.useState(null);
29196
+ const [skipMedia, setSkipMedia] = React$1.useState(false);
29197
+ const [mediaProgress, setMediaProgress] = React$1.useState(null);
29198
+ const [importMenus, setImportMenus] = React$1.useState(true);
29199
+ const [importSiteTitle, setImportSiteTitle] = React$1.useState(true);
29200
+ const [importLogo, setImportLogo] = React$1.useState(true);
29201
+ const [importSeo, setImportSeo] = React$1.useState(false);
29202
+ const [authorMappings, setAuthorMappings] = React$1.useState([]);
29203
+ const [emdashUsers, setEmDashUsers] = React$1.useState([]);
29204
+ const initializeAuthorMappings = React$1.useCallback((importAnalysis, users) => {
28617
29205
  setAuthorMappings(importAnalysis.authors.map((author) => {
28618
29206
  const matchedUser = author.email ? users.find((u) => u.email.toLowerCase() === author.email?.toLowerCase()) : void 0;
28619
29207
  return {
@@ -28625,7 +29213,7 @@ function WordPressImport() {
28625
29213
  };
28626
29214
  }));
28627
29215
  }, []);
28628
- React.useEffect(() => {
29216
+ React$1.useEffect(() => {
28629
29217
  const params = new URLSearchParams(window.location.search);
28630
29218
  const authStatus = params.get("auth");
28631
29219
  if (params.get("error") === "auth_rejected") {
@@ -31163,12 +31751,12 @@ function getUserLabel(user) {
31163
31751
  }
31164
31752
  function BylinesPage() {
31165
31753
  const queryClient = useQueryClient();
31166
- const [search, setSearch] = React.useState("");
31167
- const [guestFilter, setGuestFilter] = React.useState("all");
31168
- const [selectedId, setSelectedId] = React.useState(null);
31169
- const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
31170
- const [allItems, setAllItems] = React.useState([]);
31171
- const [nextCursor, setNextCursor] = React.useState(void 0);
31754
+ const [search, setSearch] = React$1.useState("");
31755
+ const [guestFilter, setGuestFilter] = React$1.useState("all");
31756
+ const [selectedId, setSelectedId] = React$1.useState(null);
31757
+ const [showDeleteConfirm, setShowDeleteConfirm] = React$1.useState(false);
31758
+ const [allItems, setAllItems] = React$1.useState([]);
31759
+ const [nextCursor, setNextCursor] = React$1.useState(void 0);
31172
31760
  const { data, isLoading, error } = useQuery({
31173
31761
  queryKey: [
31174
31762
  "bylines",
@@ -31181,7 +31769,7 @@ function BylinesPage() {
31181
31769
  limit: 50
31182
31770
  })
31183
31771
  });
31184
- React.useEffect(() => {
31772
+ React$1.useEffect(() => {
31185
31773
  if (data) {
31186
31774
  setAllItems(data.items);
31187
31775
  setNextCursor(data.nextCursor);
@@ -31211,8 +31799,8 @@ function BylinesPage() {
31211
31799
  });
31212
31800
  const items = allItems;
31213
31801
  const selected = items.find((item) => item.id === selectedId) ?? null;
31214
- const [form, setForm] = React.useState(() => toFormState(null));
31215
- React.useEffect(() => {
31802
+ const [form, setForm] = React$1.useState(() => toFormState(null));
31803
+ React$1.useEffect(() => {
31216
31804
  setForm(toFormState(selected));
31217
31805
  }, [selectedId, selected]);
31218
31806
  const createMutation = useMutation({
@@ -31480,14 +32068,14 @@ function RoleBadge({ role, size = "sm", showDescription = false, className }) {
31480
32068
  function UserList({ users, isLoading, hasMore, searchQuery, roleFilter, onSearchChange, onRoleFilterChange, onSelectUser, onInviteUser, onLoadMore }) {
31481
32069
  const { _: _t } = useLingui();
31482
32070
  const { roles, roleLabels } = useRolesConfig();
31483
- const roleFilterSelectItems = React.useMemo(() => ({
32071
+ const roleFilterSelectItems = React$1.useMemo(() => ({
31484
32072
  all: _t({
31485
32073
  id: "Hm90t3",
31486
32074
  message: "All roles"
31487
32075
  }),
31488
32076
  ...roleLabels
31489
32077
  }), [_t, roleLabels]);
31490
- const roleFilterSelectOptions = React.useMemo(() => [{
32078
+ const roleFilterSelectOptions = React$1.useMemo(() => [{
31491
32079
  value: "all",
31492
32080
  label: _t({
31493
32081
  id: "Hm90t3",
@@ -31710,10 +32298,10 @@ function UserListSkeleton() {
31710
32298
  function UserDetail({ user, isLoading, isOpen, isSaving, isSendingRecovery, recoverySent, recoveryError, currentUserId, onClose, onSave, onDisable, onEnable, onSendRecovery }) {
31711
32299
  const { _: _t } = useLingui();
31712
32300
  const { roles, roleLabels, getRoleLabel } = useRolesConfig();
31713
- const [name, setName] = React.useState(user?.name ?? "");
31714
- const [email, setEmail] = React.useState(user?.email ?? "");
31715
- const [role, setRole] = React.useState(user?.role ?? 30);
31716
- const userIdRef = React.useRef(user?.id);
32301
+ const [name, setName] = React$1.useState(user?.name ?? "");
32302
+ const [email, setEmail] = React$1.useState(user?.email ?? "");
32303
+ const [role, setRole] = React$1.useState(user?.role ?? 30);
32304
+ const userIdRef = React$1.useRef(user?.id);
31717
32305
  if (user?.id !== userIdRef.current) {
31718
32306
  userIdRef.current = user?.id;
31719
32307
  if (user) {
@@ -32127,12 +32715,12 @@ function UserDetailSkeleton() {
32127
32715
  function InviteUserModal({ open, isSending, error, inviteUrl, onOpenChange, onInvite }) {
32128
32716
  const { _: _t } = useLingui();
32129
32717
  const { roles, roleLabels } = useRolesConfig();
32130
- const [email, setEmail] = React.useState("");
32131
- const [role, setRole] = React.useState(30);
32132
- const [copied, setCopied] = React.useState(false);
32133
- const [copyError, setCopyError] = React.useState(false);
32134
- const copyTimeoutRef = React.useRef(void 0);
32135
- React.useEffect(() => {
32718
+ const [email, setEmail] = React$1.useState("");
32719
+ const [role, setRole] = React$1.useState(30);
32720
+ const [copied, setCopied] = React$1.useState(false);
32721
+ const [copyError, setCopyError] = React$1.useState(false);
32722
+ const copyTimeoutRef = React$1.useRef(void 0);
32723
+ React$1.useEffect(() => {
32136
32724
  if (open) {
32137
32725
  setEmail("");
32138
32726
  setRole(30);
@@ -32140,7 +32728,7 @@ function InviteUserModal({ open, isSending, error, inviteUrl, onOpenChange, onIn
32140
32728
  setCopyError(false);
32141
32729
  }
32142
32730
  }, [open]);
32143
- React.useEffect(() => {
32731
+ React$1.useEffect(() => {
32144
32732
  return () => {
32145
32733
  if (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);
32146
32734
  };
@@ -32362,8 +32950,8 @@ function InviteUserModal({ open, isSending, error, inviteUrl, onOpenChange, onIn
32362
32950
  * Debounce hook for search input
32363
32951
  */
32364
32952
  function useDebounce(value, delay) {
32365
- const [debouncedValue, setDebouncedValue] = React.useState(value);
32366
- React.useEffect(() => {
32953
+ const [debouncedValue, setDebouncedValue] = React$1.useState(value);
32954
+ React$1.useEffect(() => {
32367
32955
  const timer = setTimeout(setDebouncedValue, delay, value);
32368
32956
  return () => clearTimeout(timer);
32369
32957
  }, [value, delay]);
@@ -32372,16 +32960,16 @@ function useDebounce(value, delay) {
32372
32960
  function UsersPage() {
32373
32961
  const { getRoleLabel } = useRolesConfig();
32374
32962
  const queryClient = useQueryClient();
32375
- const [searchQuery, setSearchQuery] = React.useState("");
32376
- const [roleFilter, setRoleFilter] = React.useState();
32377
- const [selectedUserId, setSelectedUserId] = React.useState(null);
32378
- const [isDetailOpen, setIsDetailOpen] = React.useState(false);
32379
- const [isInviteOpen, setIsInviteOpen] = React.useState(false);
32380
- const [showDisableConfirm, setShowDisableConfirm] = React.useState(false);
32381
- const [showDemoteConfirm, setShowDemoteConfirm] = React.useState(false);
32382
- const [pendingSaveData, setPendingSaveData] = React.useState(null);
32383
- const [inviteError, setInviteError] = React.useState(null);
32384
- const [inviteUrl, setInviteUrl] = React.useState(null);
32963
+ const [searchQuery, setSearchQuery] = React$1.useState("");
32964
+ const [roleFilter, setRoleFilter] = React$1.useState();
32965
+ const [selectedUserId, setSelectedUserId] = React$1.useState(null);
32966
+ const [isDetailOpen, setIsDetailOpen] = React$1.useState(false);
32967
+ const [isInviteOpen, setIsInviteOpen] = React$1.useState(false);
32968
+ const [showDisableConfirm, setShowDisableConfirm] = React$1.useState(false);
32969
+ const [showDemoteConfirm, setShowDemoteConfirm] = React$1.useState(false);
32970
+ const [pendingSaveData, setPendingSaveData] = React$1.useState(null);
32971
+ const [inviteError, setInviteError] = React$1.useState(null);
32972
+ const [inviteUrl, setInviteUrl] = React$1.useState(null);
32385
32973
  const debouncedSearch = useDebounce(searchQuery, 300);
32386
32974
  const usersQuery = useInfiniteQuery({
32387
32975
  queryKey: [
@@ -32702,7 +33290,7 @@ function ContentListPage() {
32702
33290
  });
32703
33291
  const i18n = manifest?.i18n;
32704
33292
  const activeLocale = i18n ? localeParam ?? i18n.defaultLocale : void 0;
32705
- const [sort, setSort] = React.useState({
33293
+ const [sort, setSort] = React$1.useState({
32706
33294
  field: "updatedAt",
32707
33295
  direction: "desc"
32708
33296
  });
@@ -32800,7 +33388,7 @@ function ContentListPage() {
32800
33388
  });
32801
33389
  }
32802
33390
  });
32803
- const items = React.useMemo(() => {
33391
+ const items = React$1.useMemo(() => {
32804
33392
  return data?.pages.flatMap((page) => page.items) || [];
32805
33393
  }, [data]);
32806
33394
  if (!manifest) return /* @__PURE__ */ jsx(LoadingScreen, {});
@@ -32822,7 +33410,7 @@ function ContentListPage() {
32822
33410
  isLoading: isLoading || isFetchingNextPage,
32823
33411
  isTrashedLoading,
32824
33412
  hasMore: !!hasNextPage,
32825
- onLoadMore: React.useCallback(() => void fetchNextPage(), [fetchNextPage]),
33413
+ onLoadMore: React$1.useCallback(() => void fetchNextPage(), [fetchNextPage]),
32826
33414
  trashedCount: trashedData?.items?.length || 0,
32827
33415
  onDelete: (id) => deleteMutation.mutate(id),
32828
33416
  onRestore: (id) => restoreMutation.mutate(id),
@@ -32847,7 +33435,7 @@ function ContentNewPage() {
32847
33435
  const { locale } = useSearch({ from: "/_admin/content/$collection/new" });
32848
33436
  const navigate = useNavigate();
32849
33437
  const queryClient = useQueryClient();
32850
- const [selectedBylines, setSelectedBylines] = React.useState([]);
33438
+ const [selectedBylines, setSelectedBylines] = React$1.useState([]);
32851
33439
  const { data: manifest } = useQuery({
32852
33440
  queryKey: ["manifest"],
32853
33441
  queryFn: fetchManifest
@@ -32868,7 +33456,7 @@ function ContentNewPage() {
32868
33456
  });
32869
33457
  }
32870
33458
  });
32871
- const pluginBlocks = React.useMemo(() => manifest ? getPluginBlocks(manifest) : [], [manifest]);
33459
+ const pluginBlocks = React$1.useMemo(() => manifest ? getPluginBlocks(manifest) : [], [manifest]);
32872
33460
  const { data: bylinesData } = useQuery({
32873
33461
  queryKey: ["bylines"],
32874
33462
  queryFn: () => fetchBylines({ limit: 100 })
@@ -32946,7 +33534,7 @@ function ContentEditPage() {
32946
33534
  ],
32947
33535
  queryFn: () => fetchContent(collection, id)
32948
33536
  });
32949
- React.useEffect(() => {
33537
+ React$1.useEffect(() => {
32950
33538
  if (typeof searchParams.field !== "string" || isLoading) return;
32951
33539
  const timeoutId = requestIdleCallback(() => {
32952
33540
  const el = document.getElementById(`field-${searchParams.field}`);
@@ -32983,7 +33571,7 @@ function ContentEditPage() {
32983
33571
  queryFn: () => fetchRevision(rawItem.draftRevisionId),
32984
33572
  enabled: !!rawItem?.draftRevisionId
32985
33573
  });
32986
- const item = React.useMemo(() => {
33574
+ const item = React$1.useMemo(() => {
32987
33575
  if (!rawItem) return void 0;
32988
33576
  if (!draftRevision?.data) return rawItem;
32989
33577
  const draftData = {};
@@ -33056,7 +33644,7 @@ function ContentEditPage() {
33056
33644
  });
33057
33645
  }
33058
33646
  });
33059
- const [lastAutosaveAt, setLastAutosaveAt] = React.useState(null);
33647
+ const [lastAutosaveAt, setLastAutosaveAt] = React$1.useState(null);
33060
33648
  const autosaveMutation = useMutation({
33061
33649
  mutationFn: (data) => updateContent(collection, id, {
33062
33650
  ...data,
@@ -33259,7 +33847,7 @@ function ContentEditPage() {
33259
33847
  });
33260
33848
  }
33261
33849
  });
33262
- const pluginBlocks = React.useMemo(() => manifest ? getPluginBlocks(manifest) : [], [manifest]);
33850
+ const pluginBlocks = React$1.useMemo(() => manifest ? getPluginBlocks(manifest) : [], [manifest]);
33263
33851
  if (!manifest) return /* @__PURE__ */ jsx(LoadingScreen, {});
33264
33852
  const collectionConfig = manifest.collections[collection];
33265
33853
  if (!collectionConfig) return /* @__PURE__ */ jsx(NotFoundPage, { message: `Collection "${collection}" not found` });
@@ -33370,11 +33958,11 @@ function CommentsPage() {
33370
33958
  },
33371
33959
  staleTime: 300 * 1e3
33372
33960
  });
33373
- const [activeStatus, setActiveStatus] = React.useState("pending");
33374
- const [collectionFilter, setCollectionFilter] = React.useState("");
33375
- const [searchQuery, setSearchQuery] = React.useState("");
33376
- const [debouncedSearch, setDebouncedSearch] = React.useState("");
33377
- React.useEffect(() => {
33961
+ const [activeStatus, setActiveStatus] = React$1.useState("pending");
33962
+ const [collectionFilter, setCollectionFilter] = React$1.useState("");
33963
+ const [searchQuery, setSearchQuery] = React$1.useState("");
33964
+ const [debouncedSearch, setDebouncedSearch] = React$1.useState("");
33965
+ React$1.useEffect(() => {
33378
33966
  const timer = setTimeout(setDebouncedSearch, 300, searchQuery);
33379
33967
  return () => clearTimeout(timer);
33380
33968
  }, [searchQuery]);
@@ -33557,7 +34145,7 @@ function MarketplaceBrowsePage() {
33557
34145
  return fetchPlugins();
33558
34146
  }
33559
34147
  });
33560
- return /* @__PURE__ */ jsx(MarketplaceBrowse, { installedPluginIds: React.useMemo(() => {
34148
+ return /* @__PURE__ */ jsx(MarketplaceBrowse, { installedPluginIds: React$1.useMemo(() => {
33561
34149
  if (!plugins) return /* @__PURE__ */ new Set();
33562
34150
  return new Set(plugins.map((p) => p.id));
33563
34151
  }, [plugins]) });
@@ -33578,7 +34166,7 @@ function MarketplaceDetailPage() {
33578
34166
  });
33579
34167
  return /* @__PURE__ */ jsx(MarketplacePluginDetail, {
33580
34168
  pluginId,
33581
- installedPluginIds: React.useMemo(() => {
34169
+ installedPluginIds: React$1.useMemo(() => {
33582
34170
  if (!plugins) return /* @__PURE__ */ new Set();
33583
34171
  return new Set(plugins.map((p) => p.id));
33584
34172
  }, [plugins])
@@ -33611,7 +34199,10 @@ const menuListRoute = createRoute({
33611
34199
  const menuEditorRoute = createRoute({
33612
34200
  getParentRoute: () => adminLayoutRoute,
33613
34201
  path: "/menus/$name",
33614
- component: MenuEditor
34202
+ component: MenuEditor,
34203
+ validateSearch: (search) => {
34204
+ return { locale: typeof search.locale === "string" ? search.locale : void 0 };
34205
+ }
33615
34206
  });
33616
34207
  const taxonomyRoute = createRoute({
33617
34208
  getParentRoute: () => adminLayoutRoute,
@@ -33987,10 +34578,10 @@ const router = createAdminRouter(queryClient);
33987
34578
  const EMPTY_PLUGINS = {};
33988
34579
  const EMPTY_AUTH_PROVIDERS = {};
33989
34580
  function AdminApp({ pluginAdmins = EMPTY_PLUGINS, authProviders = EMPTY_AUTH_PROVIDERS, locale = "en", messages = {} }) {
33990
- React.useEffect(() => {
34581
+ React$1.useEffect(() => {
33991
34582
  document.getElementById("emdash-boot-loader")?.remove();
33992
34583
  }, []);
33993
- const i18nInitialized = React.useRef(false);
34584
+ const i18nInitialized = React$1.useRef(false);
33994
34585
  if (!i18nInitialized.current) {
33995
34586
  i18n.loadAndActivate({
33996
34587
  locale,
@@ -34014,5 +34605,5 @@ function AdminApp({ pluginAdmins = EMPTY_PLUGINS, authProviders = EMPTY_AUTH_PRO
34014
34605
  }
34015
34606
 
34016
34607
  //#endregion
34017
- export { API_BASE, API_TOKEN_SCOPES, AdminApp, AdminApp as App, AuthProviderProvider, CAPABILITY_LABELS, ContentEditor, ContentList, DEFAULT_LOCALE, Dashboard, EditorHeader, Header, Link, LoginPage, MediaLibrary, MediaPickerModal, PasskeyLogin, PasskeyRegistration, PluginAdminProvider, PortableTextEditor, SUPPORTED_LOCALES, SUPPORTED_LOCALE_CODES, SaveButton, Settings, SetupWizard, Shell, KumoSidebar as Sidebar, SidebarNav, analyzeWpPluginSite, analyzeWxr, apiFetch, bulkCommentAction, checkPluginUpdates, cn, compareRevisions, completeSignup, createAdminRouter, createAllowedDomain, createApiToken, createByline, createCollection, createContent, createField, createMenu, createMenuItem, createRedirect, createSection, createTaxonomy, createTerm, createWidget, createWidgetArea, deleteAllowedDomain, deleteByline, deleteCollection, deleteComment, deleteContent, deleteField, deleteFromProvider, deleteMedia, deleteMenu, deleteMenuItem, deletePasskey, deleteRedirect, deleteSection, deleteTerm, deleteWidget, deleteWidgetArea, describeCapability, disablePlugin, disableUser, discardDraft, duplicateContent, enablePlugin, enableUser, executeWpPluginImport, executeWxrImport, fetch404Summary, fetchAllowedDomains, fetchApiTokens, fetchAuthMode, fetchByline, fetchBylines, fetchCollection, fetchCollections, fetchComment, fetchCommentCounts, fetchComments, fetchContent, fetchContentList, fetchDashboardStats, fetchEmailSettings, fetchFields, fetchManifest, fetchMarketplacePlugin, fetchMediaList, fetchMediaProviders, fetchMenu, fetchMenus, fetchOrphanedTables, fetchPasskeys, fetchPlugin, fetchPlugins, fetchProviderMedia, fetchRedirects, fetchRevision, fetchRevisions, fetchSection, fetchSections, fetchSettings, fetchTaxonomyDef, fetchTaxonomyDefs, fetchTerms, fetchTheme, fetchTranslations, fetchTrashedContent, fetchUser, fetchUsers, fetchWidgetArea, fetchWidgetAreas, fetchWidgetComponents, generatePreviewUrl, getDraftStatus, getPreviewUrl, hasAllowedDomains, importWxrMedia, installMarketplacePlugin, inviteUser, parseApiResponse, permanentDeleteContent, prepareWxrImport, probeImportUrl, publishContent, registerOrphanedTable, renamePasskey, reorderFields, reorderMenuItems, reorderWidgets, requestSignup, resolveLocale, restoreContent, restoreRevision, revokeApiToken, rewriteContentUrls, scheduleContent, searchMarketplace, searchThemes, sendRecoveryLink, sendTestEmail, setSearchEnabled, throwResponseError, uninstallMarketplacePlugin, unpublishContent, unscheduleContent, updateAllowedDomain, updateByline, updateCollection, updateCommentStatus, updateContent, updateField, updateMarketplacePlugin, updateMedia, updateMenu, updateMenuItem, updateRedirect, updateSection, updateSettings, updateTerm, updateUser, updateWidget, uploadMedia, uploadToProvider, useAuthProviderList, useAuthProviders, useCurrentUser, useLocale, useNavigate, useParams, usePluginAdmins, usePluginField, usePluginHasPages, usePluginHasWidgets, usePluginPage, usePluginWidget, validateInviteToken, verifySignupToken };
34608
+ export { API_BASE, API_TOKEN_SCOPES, AdminApp, AdminApp as App, AuthProviderProvider, CAPABILITY_LABELS, ContentEditor, ContentList, DEFAULT_LOCALE, Dashboard, EditorHeader, Header, Link, LoginPage, MediaLibrary, MediaPickerModal, PasskeyLogin, PasskeyRegistration, PluginAdminProvider, PortableTextEditor, SUPPORTED_LOCALES, SUPPORTED_LOCALE_CODES, SaveButton, Settings, SetupWizard, Shell, KumoSidebar as Sidebar, SidebarNav, analyzeWpPluginSite, analyzeWxr, apiFetch, bulkCommentAction, checkPluginUpdates, cn, compareRevisions, completeSignup, createAdminRouter, createAllowedDomain, createApiToken, createByline, createCollection, createContent, createField, createMenu, createMenuItem, createMenuTranslation, createRedirect, createSection, createTaxonomy, createTerm, createTermTranslation, createWidget, createWidgetArea, deleteAllowedDomain, deleteByline, deleteCollection, deleteComment, deleteContent, deleteField, deleteFromProvider, deleteMedia, deleteMenu, deleteMenuItem, deletePasskey, deleteRedirect, deleteSection, deleteTerm, deleteWidget, deleteWidgetArea, describeCapability, disablePlugin, disableUser, discardDraft, duplicateContent, enablePlugin, enableUser, executeWpPluginImport, executeWxrImport, fetch404Summary, fetchAllowedDomains, fetchApiTokens, fetchAuthMode, fetchByline, fetchBylines, fetchCollection, fetchCollections, fetchComment, fetchCommentCounts, fetchComments, fetchContent, fetchContentList, fetchDashboardStats, fetchEmailSettings, fetchFields, fetchManifest, fetchMarketplacePlugin, fetchMediaList, fetchMediaProviders, fetchMenu, fetchMenuTranslations, fetchMenus, fetchOrphanedTables, fetchPasskeys, fetchPlugin, fetchPlugins, fetchProviderMedia, fetchRedirects, fetchRevision, fetchRevisions, fetchSection, fetchSections, fetchSettings, fetchTaxonomyDef, fetchTaxonomyDefs, fetchTermTranslations, fetchTerms, fetchTheme, fetchTranslations, fetchTrashedContent, fetchUser, fetchUsers, fetchWidgetArea, fetchWidgetAreas, fetchWidgetComponents, generatePreviewUrl, getDraftStatus, getPreviewUrl, hasAllowedDomains, importWxrMedia, installMarketplacePlugin, inviteUser, parseApiResponse, permanentDeleteContent, prepareWxrImport, probeImportUrl, publishContent, registerOrphanedTable, renamePasskey, reorderFields, reorderMenuItems, reorderWidgets, requestSignup, resolveLocale, restoreContent, restoreRevision, revokeApiToken, rewriteContentUrls, scheduleContent, searchMarketplace, searchThemes, sendRecoveryLink, sendTestEmail, setSearchEnabled, throwResponseError, uninstallMarketplacePlugin, unpublishContent, unscheduleContent, updateAllowedDomain, updateByline, updateCollection, updateCommentStatus, updateContent, updateField, updateMarketplacePlugin, updateMedia, updateMenu, updateMenuItem, updateRedirect, updateSection, updateSettings, updateTerm, updateUser, updateWidget, uploadMedia, uploadToProvider, useAuthProviderList, useAuthProviders, useCurrentUser, useLocale, useNavigate, useParams, usePluginAdmins, usePluginField, usePluginHasPages, usePluginHasWidgets, usePluginPage, usePluginWidget, validateInviteToken, verifySignupToken };
34018
34609
  //# sourceMappingURL=index.js.map